mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-01-08 03:21:20 +00:00
Compare commits
79 Commits
develop-20
...
develop-20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44fc00299c | ||
|
|
774fc4281c | ||
|
|
c47955c069 | ||
|
|
e7569644f7 | ||
|
|
c567474043 | ||
|
|
9f394e92fe | ||
|
|
66befc7e44 | ||
|
|
c3a28fc698 | ||
|
|
ef317d5b3c | ||
|
|
152301f9ee | ||
|
|
645e9ba1f7 | ||
|
|
56487c3a33 | ||
|
|
b8062a915c | ||
|
|
5780c9512a | ||
|
|
71d39707d9 | ||
|
|
9ccb8ae692 | ||
|
|
8cd50bb5bd | ||
|
|
ae9e1278e5 | ||
|
|
58c03797b2 | ||
|
|
7db38b4c6c | ||
|
|
da6b447e64 | ||
|
|
c19ac2b0f3 | ||
|
|
d5ca2171b3 | ||
|
|
20972cb29f | ||
|
|
7b714d0866 | ||
|
|
240ae8fa57 | ||
|
|
5a2f6b2652 | ||
|
|
4196ce31f0 | ||
|
|
be8ca5db50 | ||
|
|
30a417ea3c | ||
|
|
695ed940e0 | ||
|
|
1353554cf8 | ||
|
|
e1ba2732af | ||
|
|
42b57c0e0e | ||
|
|
a6072753b2 | ||
|
|
e92c224c39 | ||
|
|
a3ed7ec8f6 | ||
|
|
17a2f99dff | ||
|
|
c14971543c | ||
|
|
55f899608d | ||
|
|
83be63f27e | ||
|
|
ed48d190e5 | ||
|
|
3c3b6615e6 | ||
|
|
e71e5a877b | ||
|
|
b2a65dc660 | ||
|
|
d66dccd076 | ||
|
|
c1128b28f2 | ||
|
|
da8e78c28d | ||
|
|
f50aa6b0ce | ||
|
|
661e4e53e6 | ||
|
|
3eeda4a6aa | ||
|
|
4dba9cea21 | ||
|
|
6aab5fab05 | ||
|
|
4b0597d19a | ||
|
|
92f534bcb3 | ||
|
|
76e91be4dc | ||
|
|
deca4fed56 | ||
|
|
73512b0365 | ||
|
|
aaffc125e7 | ||
|
|
41a48c39a0 | ||
|
|
2d96bd84b5 | ||
|
|
ad1c1d2254 | ||
|
|
813206766d | ||
|
|
bb25d4a82a | ||
|
|
f3b78beecc | ||
|
|
64073768fe | ||
|
|
fe6dd0f901 | ||
|
|
aac8d11ff6 | ||
|
|
afa99a35b5 | ||
|
|
e9cb0a51d7 | ||
|
|
9fbcccfd02 | ||
|
|
468c9c9d56 | ||
|
|
f76b27a73d | ||
|
|
579fe81616 | ||
|
|
ec9ba53690 | ||
|
|
85337c53d4 | ||
|
|
eb6d585bb2 | ||
|
|
378ffbc609 | ||
|
|
3b3c8e5bcd |
@@ -31,6 +31,7 @@ use FireflyIII\Exceptions\FireflyException;
|
|||||||
use FireflyIII\Models\Account;
|
use FireflyIII\Models\Account;
|
||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Support\Debug\Timer;
|
use FireflyIII\Support\Debug\Timer;
|
||||||
|
use FireflyIII\Support\Facades\Amount;
|
||||||
use FireflyIII\Support\Facades\Steam;
|
use FireflyIII\Support\Facades\Steam;
|
||||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
@@ -79,17 +80,20 @@ class AccountController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function accounts(AutocompleteRequest $request): JsonResponse
|
public function accounts(AutocompleteRequest $request): JsonResponse
|
||||||
{
|
{
|
||||||
$data = $request->getData();
|
$data = $request->getData();
|
||||||
$types = $data['types'];
|
$types = $data['types'];
|
||||||
$query = $data['query'];
|
$query = $data['query'];
|
||||||
$date = $data['date'] ?? today(config('app.timezone'));
|
$date = $data['date'] ?? today(config('app.timezone'));
|
||||||
$return = [];
|
$return = [];
|
||||||
Timer::start(sprintf('AC accounts "%s"', $query));
|
$timer = Timer::getInstance();
|
||||||
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
|
$timer->start(sprintf('AC accounts "%s"', $query));
|
||||||
|
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
|
||||||
|
|
||||||
// set date to subday + end-of-day for account balance. so it is at $date 23:59:59
|
// set date to subday + end-of-day for account balance. so it is at $date 23:59:59
|
||||||
$date->endOfDay();
|
$date->endOfDay();
|
||||||
|
|
||||||
|
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
|
|
||||||
/** @var Account $account */
|
/** @var Account $account */
|
||||||
foreach ($result as $account) {
|
foreach ($result as $account) {
|
||||||
$nameWithBalance = $account->name;
|
$nameWithBalance = $account->name;
|
||||||
@@ -98,15 +102,11 @@ class AccountController extends Controller
|
|||||||
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
|
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
|
||||||
// this one is correct.
|
// this one is correct.
|
||||||
Log::debug(sprintf('accounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
Log::debug(sprintf('accounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||||
$balance = Steam::finalAccountBalance($account, $date);
|
$balance = $allBalances[$account->id] ?? [];
|
||||||
$key = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? 'pc_balance' : 'balance';
|
$key = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? 'pc_balance' : 'balance';
|
||||||
$useCurrency = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? $this->primaryCurrency : $currency;
|
$useCurrency = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? $this->primaryCurrency : $currency;
|
||||||
$amount = $balance[$key] ?? '0';
|
$amount = $balance[$key] ?? '0';
|
||||||
$nameWithBalance = sprintf(
|
$nameWithBalance = sprintf('%s (%s)', $account->name, Amount::formatAnything($useCurrency, $amount, false));
|
||||||
'%s (%s)',
|
|
||||||
$account->name,
|
|
||||||
app('amount')->formatAnything($useCurrency, $amount, false)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$return[] = [
|
$return[] = [
|
||||||
@@ -138,7 +138,7 @@ class AccountController extends Controller
|
|||||||
return $posA - $posB;
|
return $posA - $posB;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
Timer::stop(sprintf('AC accounts "%s"', $query));
|
$timer->stop(sprintf('AC accounts "%s"', $query));
|
||||||
|
|
||||||
return response()->api($return);
|
return response()->api($return);
|
||||||
}
|
}
|
||||||
|
|||||||
97
app/Api/V1/Controllers/Chart/BalanceController.php
Normal file
97
app/Api/V1/Controllers/Chart/BalanceController.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Api\V1\Controllers\Chart;
|
||||||
|
|
||||||
|
use FireflyIII\Api\V1\Controllers\Controller;
|
||||||
|
use FireflyIII\Api\V1\Requests\Chart\ChartRequest;
|
||||||
|
use FireflyIII\Enums\TransactionTypeEnum;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Chart\ChartData;
|
||||||
|
use FireflyIII\Support\Facades\Amount;
|
||||||
|
use FireflyIII\Support\Http\Api\AccountBalanceGrouped;
|
||||||
|
use FireflyIII\Support\Http\Api\CleansChartData;
|
||||||
|
use FireflyIII\Support\Http\Api\CollectsAccountsFromFilter;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BalanceController
|
||||||
|
*/
|
||||||
|
class BalanceController extends Controller
|
||||||
|
{
|
||||||
|
use CleansChartData;
|
||||||
|
use CollectsAccountsFromFilter;
|
||||||
|
|
||||||
|
private ChartData $chartData;
|
||||||
|
private GroupCollectorInterface $collector;
|
||||||
|
private AccountRepositoryInterface $repository;
|
||||||
|
|
||||||
|
// private TransactionCurrency $default;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
parent::__construct();
|
||||||
|
$this->middleware(
|
||||||
|
function ($request, $next) {
|
||||||
|
$this->repository = app(AccountRepositoryInterface::class);
|
||||||
|
$this->collector = app(GroupCollectorInterface::class);
|
||||||
|
$userGroup = $this->validateUserGroup($request);
|
||||||
|
$this->repository->setUserGroup($userGroup);
|
||||||
|
$this->collector->setUserGroup($userGroup);
|
||||||
|
$this->chartData = new ChartData();
|
||||||
|
// $this->default = app('amount')->getPrimaryCurrency();
|
||||||
|
|
||||||
|
return $next($request);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The code is practically a duplicate of ReportController::operations.
|
||||||
|
*
|
||||||
|
* Currency is up to the account/transactions in question, but conversion to the default
|
||||||
|
* currency is possible.
|
||||||
|
*
|
||||||
|
* If the transaction being processed is already in native currency OR if the
|
||||||
|
* foreign amount is in the native currency, the amount will not be converted.
|
||||||
|
*
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function balance(ChartRequest $request): JsonResponse
|
||||||
|
{
|
||||||
|
$queryParameters = $request->getParameters();
|
||||||
|
$accounts = $this->getAccountList($queryParameters);
|
||||||
|
|
||||||
|
// prepare for currency conversion and data collection:
|
||||||
|
/** @var TransactionCurrency $primary */
|
||||||
|
$primary = Amount::getPrimaryCurrency();
|
||||||
|
|
||||||
|
// get journals for entire period:
|
||||||
|
|
||||||
|
$this->collector->setRange($queryParameters['start'], $queryParameters['end'])
|
||||||
|
->withAccountInformation()
|
||||||
|
->setXorAccounts($accounts)
|
||||||
|
->setTypes([TransactionTypeEnum::WITHDRAWAL->value, TransactionTypeEnum::DEPOSIT->value, TransactionTypeEnum::RECONCILIATION->value, TransactionTypeEnum::TRANSFER->value])
|
||||||
|
;
|
||||||
|
$journals = $this->collector->getExtractedJournals();
|
||||||
|
|
||||||
|
$object = new AccountBalanceGrouped();
|
||||||
|
$object->setPreferredRange($queryParameters['period']);
|
||||||
|
$object->setPrimary($primary);
|
||||||
|
$object->setAccounts($accounts);
|
||||||
|
$object->setJournals($journals);
|
||||||
|
$object->setStart($queryParameters['start']);
|
||||||
|
$object->setEnd($queryParameters['end']);
|
||||||
|
$object->groupByCurrencyAndPeriod();
|
||||||
|
$data = $object->convertToChartData();
|
||||||
|
foreach ($data as $entry) {
|
||||||
|
$this->chartData->add($entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response()->json($this->chartData->render());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -248,10 +248,10 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatAnything($currency, $sums[$currencyId]['sum'] ?? '0', false),
|
'value_parsed' => Amount::formatAnything($currency, $sums[$currencyId]['sum'] ?? '0', false),
|
||||||
'local_icon' => 'balance-scale',
|
'local_icon' => 'balance-scale',
|
||||||
'sub_title' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false)
|
'sub_title' => Amount::formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false)
|
||||||
.' + '.app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
|
.' + '.Amount::formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
|
||||||
];
|
];
|
||||||
$return[] = [
|
$return[] = [
|
||||||
'key' => sprintf('spent-in-%s', $currency->code),
|
'key' => sprintf('spent-in-%s', $currency->code),
|
||||||
@@ -261,7 +261,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false),
|
'value_parsed' => Amount::formatAnything($currency, $expenses[$currencyId]['sum'] ?? '0', false),
|
||||||
'local_icon' => 'balance-scale',
|
'local_icon' => 'balance-scale',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -273,7 +273,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
|
'value_parsed' => Amount::formatAnything($currency, $incomes[$currencyId]['sum'] ?? '0', false),
|
||||||
'local_icon' => 'balance-scale',
|
'local_icon' => 'balance-scale',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -289,10 +289,10 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatAnything($currency, '0', false),
|
'value_parsed' => Amount::formatAnything($currency, '0', false),
|
||||||
'local_icon' => 'balance-scale',
|
'local_icon' => 'balance-scale',
|
||||||
'sub_title' => app('amount')->formatAnything($currency, '0', false)
|
'sub_title' => Amount::formatAnything($currency, '0', false)
|
||||||
.' + '.app('amount')->formatAnything($currency, '0', false),
|
.' + '.Amount::formatAnything($currency, '0', false),
|
||||||
];
|
];
|
||||||
$return[] = [
|
$return[] = [
|
||||||
'key' => sprintf('spent-in-%s', $currency->code),
|
'key' => sprintf('spent-in-%s', $currency->code),
|
||||||
@@ -302,7 +302,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatAnything($currency, '0', false),
|
'value_parsed' => Amount::formatAnything($currency, '0', false),
|
||||||
'local_icon' => 'balance-scale',
|
'local_icon' => 'balance-scale',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -314,7 +314,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatAnything($currency, '0', false),
|
'value_parsed' => Amount::formatAnything($currency, '0', false),
|
||||||
'local_icon' => 'balance-scale',
|
'local_icon' => 'balance-scale',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -405,7 +405,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $info['code'],
|
'currency_code' => $info['code'],
|
||||||
'currency_symbol' => $info['symbol'],
|
'currency_symbol' => $info['symbol'],
|
||||||
'currency_decimal_places' => $info['decimal_places'],
|
'currency_decimal_places' => $info['decimal_places'],
|
||||||
'value_parsed' => app('amount')->formatFlat($info['symbol'], $info['decimal_places'], $amount, false),
|
'value_parsed' => Amount::formatFlat($info['symbol'], $info['decimal_places'], $amount, false),
|
||||||
'local_icon' => 'check',
|
'local_icon' => 'check',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -424,7 +424,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $info['code'],
|
'currency_code' => $info['code'],
|
||||||
'currency_symbol' => $info['symbol'],
|
'currency_symbol' => $info['symbol'],
|
||||||
'currency_decimal_places' => $info['decimal_places'],
|
'currency_decimal_places' => $info['decimal_places'],
|
||||||
'value_parsed' => app('amount')->formatFlat($info['symbol'], $info['decimal_places'], $amount, false),
|
'value_parsed' => Amount::formatFlat($info['symbol'], $info['decimal_places'], $amount, false),
|
||||||
'local_icon' => 'calendar-o',
|
'local_icon' => 'calendar-o',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -443,7 +443,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
|
'value_parsed' => Amount::formatFlat($currency->symbol, $currency->decimal_places, '0', false),
|
||||||
'local_icon' => 'check',
|
'local_icon' => 'check',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -455,7 +455,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_decimal_places' => $currency->decimal_places,
|
'currency_decimal_places' => $currency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
|
'value_parsed' => Amount::formatFlat($currency->symbol, $currency->decimal_places, '0', false),
|
||||||
'local_icon' => 'calendar-o',
|
'local_icon' => 'calendar-o',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -493,14 +493,9 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $currencies[$currencyId]->code,
|
'currency_code' => $currencies[$currencyId]->code,
|
||||||
'currency_symbol' => $currencies[$currencyId]->symbol,
|
'currency_symbol' => $currencies[$currencyId]->symbol,
|
||||||
'currency_decimal_places' => $currencies[$currencyId]->decimal_places,
|
'currency_decimal_places' => $currencies[$currencyId]->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatFlat($currencies[$currencyId]->symbol, $currencies[$currencyId]->decimal_places, $availableBudget, false),
|
'value_parsed' => Amount::formatFlat($currencies[$currencyId]->symbol, $currencies[$currencyId]->decimal_places, $availableBudget, false),
|
||||||
'local_icon' => 'money',
|
'local_icon' => 'money',
|
||||||
'sub_title' => app('amount')->formatFlat(
|
'sub_title' => Amount::formatFlat($currencies[$currencyId]->symbol, $currencies[$currencyId]->decimal_places, $availableBudget, false),
|
||||||
$currencies[$currencyId]->symbol,
|
|
||||||
$currencies[$currencyId]->decimal_places,
|
|
||||||
$availableBudget,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
foreach ($spent as $row) {
|
foreach ($spent as $row) {
|
||||||
@@ -529,18 +524,14 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $row['currency_code'],
|
'currency_code' => $row['currency_code'],
|
||||||
'currency_symbol' => $row['currency_symbol'],
|
'currency_symbol' => $row['currency_symbol'],
|
||||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||||
'value_parsed' => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false),
|
'value_parsed' => Amount::formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $leftToSpend, false),
|
||||||
'local_icon' => 'money',
|
'local_icon' => 'money',
|
||||||
'sub_title' => app('amount')->formatFlat(
|
'sub_title' => Amount::formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $perDay, false),
|
||||||
$row['currency_symbol'],
|
|
||||||
$row['currency_decimal_places'],
|
|
||||||
$perDay,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
unset($leftToSpend);
|
unset($leftToSpend);
|
||||||
if (0 === count($return)) {
|
if (0 === count($return)) {
|
||||||
|
$days = (int) $start->diffInDays($end, true) + 1;
|
||||||
// a small trick to get every expense in this period, regardless of budget.
|
// a small trick to get every expense in this period, regardless of budget.
|
||||||
$spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection());
|
$spent = $this->opsRepository->sumExpenses($start, $end, null, new Collection());
|
||||||
foreach ($spent as $row) {
|
foreach ($spent as $row) {
|
||||||
@@ -563,14 +554,9 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $row['currency_code'],
|
'currency_code' => $row['currency_code'],
|
||||||
'currency_symbol' => $row['currency_symbol'],
|
'currency_symbol' => $row['currency_symbol'],
|
||||||
'currency_decimal_places' => $row['currency_decimal_places'],
|
'currency_decimal_places' => $row['currency_decimal_places'],
|
||||||
'value_parsed' => app('amount')->formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $spentInCurrency, false),
|
'value_parsed' => Amount::formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $spentInCurrency, false),
|
||||||
'local_icon' => 'money',
|
'local_icon' => 'money',
|
||||||
'sub_title' => app('amount')->formatFlat(
|
'sub_title' => Amount::formatFlat($row['currency_symbol'], $row['currency_decimal_places'], $perDay, false),
|
||||||
$row['currency_symbol'],
|
|
||||||
$row['currency_decimal_places'],
|
|
||||||
$perDay,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,9 +573,9 @@ class BasicController extends Controller
|
|||||||
// 'currency_code' => $currency->code,
|
// 'currency_code' => $currency->code,
|
||||||
// 'currency_symbol' => $currency->symbol,
|
// 'currency_symbol' => $currency->symbol,
|
||||||
// 'currency_decimal_places' => $currency->decimal_places,
|
// 'currency_decimal_places' => $currency->decimal_places,
|
||||||
// 'value_parsed' => app('amount')->formatFlat($currency->symbol, $currency->decimal_places, '0', false),
|
// 'value_parsed' => Amount::formatFlat($currency->symbol, $currency->decimal_places, '0', false),
|
||||||
// 'local_icon' => 'money',
|
// 'local_icon' => 'money',
|
||||||
// 'sub_title' => app('amount')->formatFlat(
|
// 'sub_title' => Amount::formatFlat(
|
||||||
// $currency->symbol,
|
// $currency->symbol,
|
||||||
// $currency->decimal_places,
|
// $currency->decimal_places,
|
||||||
// '0',
|
// '0',
|
||||||
@@ -642,7 +628,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $data['currency_code'],
|
'currency_code' => $data['currency_code'],
|
||||||
'currency_symbol' => $data['currency_symbol'],
|
'currency_symbol' => $data['currency_symbol'],
|
||||||
'currency_decimal_places' => $data['currency_decimal_places'],
|
'currency_decimal_places' => $data['currency_decimal_places'],
|
||||||
'value_parsed' => app('amount')->formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false),
|
'value_parsed' => Amount::formatFlat($data['currency_symbol'], $data['currency_decimal_places'], $data['balance'], false),
|
||||||
'local_icon' => 'line-chart',
|
'local_icon' => 'line-chart',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
@@ -656,7 +642,7 @@ class BasicController extends Controller
|
|||||||
'currency_code' => $this->primaryCurrency->code,
|
'currency_code' => $this->primaryCurrency->code,
|
||||||
'currency_symbol' => $this->primaryCurrency->symbol,
|
'currency_symbol' => $this->primaryCurrency->symbol,
|
||||||
'currency_decimal_places' => $this->primaryCurrency->decimal_places,
|
'currency_decimal_places' => $this->primaryCurrency->decimal_places,
|
||||||
'value_parsed' => app('amount')->formatFlat($this->primaryCurrency->symbol, $this->primaryCurrency->decimal_places, '0', false),
|
'value_parsed' => Amount::formatFlat($this->primaryCurrency->symbol, $this->primaryCurrency->decimal_places, '0', false),
|
||||||
'local_icon' => 'line-chart',
|
'local_icon' => 'line-chart',
|
||||||
'sub_title' => '',
|
'sub_title' => '',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ class CronController extends Controller
|
|||||||
$return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']);
|
$return['exchange_rates'] = $this->exchangeRatesCronJob($config['force'], $config['date']);
|
||||||
}
|
}
|
||||||
$return['bill_notifications'] = $this->billWarningCronJob($config['force'], $config['date']);
|
$return['bill_notifications'] = $this->billWarningCronJob($config['force'], $config['date']);
|
||||||
|
$return['webhooks'] = $this->webhookCronJob($config['force'], $config['date']);
|
||||||
|
|
||||||
return response()->api($return);
|
return response()->api($return);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ use FireflyIII\Models\TransactionCurrency;
|
|||||||
use FireflyIII\Models\UserGroup;
|
use FireflyIII\Models\UserGroup;
|
||||||
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
|
||||||
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
|
use FireflyIII\Repositories\UserGroup\UserGroupRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Facades\Amount;
|
||||||
use FireflyIII\Support\Facades\Preferences;
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
@@ -72,6 +73,8 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
|||||||
/** @var UserGroupRepositoryInterface $repository */
|
/** @var UserGroupRepositoryInterface $repository */
|
||||||
$repository = app(UserGroupRepositoryInterface::class);
|
$repository = app(UserGroupRepositoryInterface::class);
|
||||||
|
|
||||||
|
Preferences::mark();
|
||||||
|
|
||||||
/** @var UserGroup $userGroup */
|
/** @var UserGroup $userGroup */
|
||||||
foreach ($repository->getAll() as $userGroup) {
|
foreach ($repository->getAll() as $userGroup) {
|
||||||
$this->recalculateForGroup($userGroup);
|
$this->recalculateForGroup($userGroup);
|
||||||
@@ -87,8 +90,7 @@ class CorrectsPrimaryCurrencyAmounts extends Command
|
|||||||
$this->recalculateAccounts($userGroup);
|
$this->recalculateAccounts($userGroup);
|
||||||
|
|
||||||
// do a check with the group's currency so we can skip some stuff.
|
// do a check with the group's currency so we can skip some stuff.
|
||||||
Preferences::mark();
|
$currency = Amount::getPrimaryCurrencyByUserGroup($userGroup);
|
||||||
$currency = app('amount')->getPrimaryCurrencyByUserGroup($userGroup);
|
|
||||||
|
|
||||||
$this->recalculatePiggyBanks($userGroup, $currency);
|
$this->recalculatePiggyBanks($userGroup, $currency);
|
||||||
$this->recalculateBudgets($userGroup, $currency);
|
$this->recalculateBudgets($userGroup, $currency);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use FireflyIII\Support\Cronjobs\BillWarningCronjob;
|
|||||||
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
|
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
|
||||||
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
||||||
use FireflyIII\Support\Cronjobs\UpdateCheckCronjob;
|
use FireflyIII\Support\Cronjobs\UpdateCheckCronjob;
|
||||||
|
use FireflyIII\Support\Cronjobs\WebhookCronjob;
|
||||||
use Illuminate\Console\Command;
|
use Illuminate\Console\Command;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
@@ -50,6 +51,7 @@ class Cron extends Command
|
|||||||
{--create-recurring : Create recurring transactions. Other tasks will be skipped unless also requested.}
|
{--create-recurring : Create recurring transactions. Other tasks will be skipped unless also requested.}
|
||||||
{--create-auto-budgets : Create auto budgets. Other tasks will be skipped unless also requested.}
|
{--create-auto-budgets : Create auto budgets. Other tasks will be skipped unless also requested.}
|
||||||
{--send-bill-warnings : Send bill warnings. Other tasks will be skipped unless also requested.}
|
{--send-bill-warnings : Send bill warnings. Other tasks will be skipped unless also requested.}
|
||||||
|
{--send-webhook-messages : Sends any stray webhook messages (with a maximum of 5).}
|
||||||
';
|
';
|
||||||
|
|
||||||
public function handle(): int
|
public function handle(): int
|
||||||
@@ -58,7 +60,8 @@ class Cron extends Command
|
|||||||
&& !$this->option('create-recurring')
|
&& !$this->option('create-recurring')
|
||||||
&& !$this->option('create-auto-budgets')
|
&& !$this->option('create-auto-budgets')
|
||||||
&& !$this->option('send-bill-warnings')
|
&& !$this->option('send-bill-warnings')
|
||||||
&& !$this->option('check-version');
|
&& !$this->option('check-version')
|
||||||
|
&& !$this->option('send-webhook-messages');
|
||||||
$date = null;
|
$date = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -122,6 +125,16 @@ class Cron extends Command
|
|||||||
$this->friendlyError($e->getMessage());
|
$this->friendlyError($e->getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Fire webhook messages cron job.
|
||||||
|
if ($doAll || $this->option('send-webhook-messages')) {
|
||||||
|
try {
|
||||||
|
$this->webhookCronJob($force, $date);
|
||||||
|
} catch (FireflyException $e) {
|
||||||
|
app('log')->error($e->getMessage());
|
||||||
|
app('log')->error($e->getTraceAsString());
|
||||||
|
$this->friendlyError($e->getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$this->friendlyInfo('More feedback on the cron jobs can be found in the log files.');
|
$this->friendlyInfo('More feedback on the cron jobs can be found in the log files.');
|
||||||
|
|
||||||
@@ -239,4 +252,26 @@ class Cron extends Command
|
|||||||
$this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
|
$this->friendlyPositive(sprintf('"Send bill warnings" cron ran with success: %s', $autoBudget->message));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function webhookCronJob(bool $force, ?Carbon $date): void
|
||||||
|
{
|
||||||
|
$webhook = new WebhookCronjob();
|
||||||
|
$webhook->setForce($force);
|
||||||
|
// set date in cron job:
|
||||||
|
if ($date instanceof Carbon) {
|
||||||
|
$webhook->setDate($date);
|
||||||
|
}
|
||||||
|
|
||||||
|
$webhook->fire();
|
||||||
|
|
||||||
|
if ($webhook->jobErrored) {
|
||||||
|
$this->friendlyError(sprintf('Error in "webhook" cron: %s', $webhook->message));
|
||||||
|
}
|
||||||
|
if ($webhook->jobFired) {
|
||||||
|
$this->friendlyInfo(sprintf('"Webhook" cron fired: %s', $webhook->message));
|
||||||
|
}
|
||||||
|
if ($webhook->jobSucceeded) {
|
||||||
|
$this->friendlyPositive(sprintf('"Webhook" cron ran with success: %s', $webhook->message));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
app/Events/Model/Bill/WarnUserAboutBill.php
Normal file
20
app/Events/Model/Bill/WarnUserAboutBill.php
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Events\Model\Bill;
|
||||||
|
|
||||||
|
use FireflyIII\Events\Event;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WarnUserAboutBill.
|
||||||
|
*/
|
||||||
|
class WarnUserAboutBill extends Event
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public Bill $bill, public string $field, public int $diff) {}
|
||||||
|
}
|
||||||
17
app/Events/Model/Bill/WarnUserAboutOverdueSubscriptions.php
Normal file
17
app/Events/Model/Bill/WarnUserAboutOverdueSubscriptions.php
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Events\Model\Bill;
|
||||||
|
|
||||||
|
use FireflyIII\Events\Event;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
|
class WarnUserAboutOverdueSubscriptions extends Event
|
||||||
|
{
|
||||||
|
use SerializesModels;
|
||||||
|
|
||||||
|
public function __construct(public User $user, public array $overdue) {}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -35,6 +35,6 @@ class UserGroupChangedPrimaryCurrency extends Event
|
|||||||
|
|
||||||
public function __construct(public UserGroup $userGroup)
|
public function __construct(public UserGroup $userGroup)
|
||||||
{
|
{
|
||||||
Log::debug('User group changed default currency.');
|
Log::debug('User group changed primary currency.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DestroyedTransactionGroup.php
|
|
||||||
* Copyright (c) 2019 james@firefly-iii.org
|
|
||||||
*
|
|
||||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as
|
|
||||||
* published by the Free Software Foundation, either version 3 of the
|
|
||||||
* License, or (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace FireflyIII\Events;
|
|
||||||
|
|
||||||
use FireflyIII\Models\Bill;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class WarnUserAboutBill.
|
|
||||||
*/
|
|
||||||
class WarnUserAboutBill extends Event
|
|
||||||
{
|
|
||||||
use SerializesModels;
|
|
||||||
|
|
||||||
public function __construct(public Bill $bill, public string $field, public int $diff) {}
|
|
||||||
}
|
|
||||||
@@ -24,48 +24,118 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Handlers\Events;
|
namespace FireflyIII\Handlers\Events;
|
||||||
|
|
||||||
use FireflyIII\Events\WarnUserAboutBill;
|
|
||||||
use FireflyIII\Notifications\User\BillReminder;
|
|
||||||
use Illuminate\Support\Facades\Notification;
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
||||||
|
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
||||||
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Notifications\User\BillReminder;
|
||||||
|
use FireflyIII\Notifications\User\SubscriptionsOverdueReminder;
|
||||||
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Illuminate\Support\Facades\Notification;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BillEventHandler
|
* Class BillEventHandler
|
||||||
*/
|
*/
|
||||||
class BillEventHandler
|
class BillEventHandler
|
||||||
{
|
{
|
||||||
|
public function warnAboutOverdueSubscriptions(WarnUserAboutOverdueSubscriptions $event): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||||
|
// make sure user does not get the warning twice.
|
||||||
|
$overdue = $event->overdue;
|
||||||
|
$user = $event->user;
|
||||||
|
$toBeWarned = [];
|
||||||
|
Log::debug(sprintf('%d bills to warn about.', count($overdue)));
|
||||||
|
foreach ($overdue as $item) {
|
||||||
|
/** @var Bill $bill */
|
||||||
|
$bill = $item['bill'];
|
||||||
|
$key = sprintf('bill_overdue_%s_%s', $bill->id, substr(hash('sha256', json_encode($item['dates']['pay_dates'], JSON_THROW_ON_ERROR)), 0, 10));
|
||||||
|
$pref = Preferences::getForUser($bill->user, $key, false);
|
||||||
|
if (true === $pref->data) {
|
||||||
|
Log::debug(sprintf('User #%d has already been warned about overdue subscription #%d.', $bill->user->id, $bill->id));
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$toBeWarned[] = $item;
|
||||||
|
}
|
||||||
|
unset($bill);
|
||||||
|
Log::debug(sprintf('Now %d bills to warn about.', count($toBeWarned)));
|
||||||
|
|
||||||
|
/** @var bool $sendNotification */
|
||||||
|
$sendNotification = Preferences::getForUser($user, 'notification_bill_reminder', true)->data;
|
||||||
|
if (false === $sendNotification) {
|
||||||
|
Log::debug('User has disabled bill reminders.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log::debug(sprintf('Will warn about %d overdue subscription(s).', count($toBeWarned)));
|
||||||
|
if (0 === count($toBeWarned)) {
|
||||||
|
Log::debug('No overdue subscriptions to warn about.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
foreach ($toBeWarned as $item) {
|
||||||
|
/** @var Bill $bill */
|
||||||
|
$bill = $item['bill'];
|
||||||
|
$key = sprintf('bill_overdue_%s_%s', $bill->id, substr(hash('sha256', json_encode($item['dates']['pay_dates'], JSON_THROW_ON_ERROR)), 0, 10));
|
||||||
|
Preferences::setForUser($bill->user, $key, true);
|
||||||
|
}
|
||||||
|
Log::warning('should hit this ONCE');
|
||||||
|
|
||||||
|
try {
|
||||||
|
Notification::send($user, new SubscriptionsOverdueReminder($toBeWarned));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$message = $e->getMessage();
|
||||||
|
if (str_contains($message, 'Bcc')) {
|
||||||
|
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (str_contains($message, 'RFC 2822')) {
|
||||||
|
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Log::error($e->getMessage());
|
||||||
|
Log::error($e->getTraceAsString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public function warnAboutBill(WarnUserAboutBill $event): void
|
public function warnAboutBill(WarnUserAboutBill $event): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||||
|
|
||||||
$bill = $event->bill;
|
$bill = $event->bill;
|
||||||
|
|
||||||
/** @var bool $preference */
|
/** @var bool $preference */
|
||||||
$preference = app('preferences')->getForUser($bill->user, 'notification_bill_reminder', true)->data;
|
Preferences::getForUser($bill->user, 'notification_bill_reminder', true)->data;
|
||||||
|
|
||||||
if (true === $preference) {
|
if (true === $preference) {
|
||||||
app('log')->debug('Bill reminder is true!');
|
Log::debug('Bill reminder is true!');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff));
|
Notification::send($bill->user, new BillReminder($bill, $event->field, $event->diff));
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
$message = $e->getMessage();
|
$message = $e->getMessage();
|
||||||
if (str_contains($message, 'Bcc')) {
|
if (str_contains($message, 'Bcc')) {
|
||||||
app('log')->warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
Log::warning('[Bcc] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (str_contains($message, 'RFC 2822')) {
|
if (str_contains($message, 'RFC 2822')) {
|
||||||
app('log')->warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
Log::warning('[RFC] Could not send notification. Please validate your email settings, use the .env.example file as a guide.');
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('log')->error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
app('log')->error($e->getTraceAsString());
|
Log::error($e->getTraceAsString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
if (false === $preference) {
|
Log::debug('User has disabled bill reminders.');
|
||||||
app('log')->debug('User has disabled bill reminders.');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ class PreferencesEventHandler
|
|||||||
$repository = app(PiggyBankRepositoryInterface::class);
|
$repository = app(PiggyBankRepositoryInterface::class);
|
||||||
$repository->setUserGroup($userGroup);
|
$repository->setUserGroup($userGroup);
|
||||||
$piggyBanks = $repository->getPiggyBanks();
|
$piggyBanks = $repository->getPiggyBanks();
|
||||||
|
Log::debug(sprintf('Resetting %d piggy bank(s).', $piggyBanks->count()));
|
||||||
|
|
||||||
/** @var PiggyBank $piggyBank */
|
/** @var PiggyBank $piggyBank */
|
||||||
foreach ($piggyBanks as $piggyBank) {
|
foreach ($piggyBanks as $piggyBank) {
|
||||||
@@ -104,6 +105,8 @@ class PreferencesEventHandler
|
|||||||
$repository->setUserGroup($userGroup);
|
$repository->setUserGroup($userGroup);
|
||||||
$set = $repository->getBudgets();
|
$set = $repository->getBudgets();
|
||||||
|
|
||||||
|
Log::debug(sprintf('Resetting %d budget(s).', $set->count()));
|
||||||
|
|
||||||
/** @var Budget $budget */
|
/** @var Budget $budget */
|
||||||
foreach ($set as $budget) {
|
foreach ($set as $budget) {
|
||||||
foreach ($budget->autoBudgets as $autoBudget) {
|
foreach ($budget->autoBudgets as $autoBudget) {
|
||||||
|
|||||||
@@ -77,8 +77,8 @@ class NetWorth implements NetWorthInterface
|
|||||||
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('Now in byAccounts("%s", "%s")', $ids, $date->format('Y-m-d H:i:s')));
|
||||||
$primary = Amount::getPrimaryCurrency();
|
$primary = Amount::getPrimaryCurrency();
|
||||||
$netWorth = [];
|
$netWorth = [];
|
||||||
Log::debug(sprintf('NetWorth: finalAccountsBalance("%s")', $date->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('NetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
|
||||||
$balances = Steam::finalAccountsBalance($accounts, $date);
|
$balances = Steam::accountsBalancesOptimized($accounts, $date, null, $convertToPrimary);
|
||||||
|
|
||||||
/** @var Account $account */
|
/** @var Account $account */
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
@@ -143,8 +143,8 @@ class NetWorth implements NetWorthInterface
|
|||||||
*/
|
*/
|
||||||
$accounts = $this->getAccounts();
|
$accounts = $this->getAccounts();
|
||||||
$return = [];
|
$return = [];
|
||||||
Log::debug(sprintf('SumNetWorth: finalAccountsBalance("%s")', $date->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('SumNetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
|
||||||
$balances = Steam::finalAccountsBalance($accounts, $date);
|
$balances = Steam::accountsBalancesOptimized($accounts, $date);
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
$currency = $this->accountRepository->getAccountCurrency($account);
|
$currency = $this->accountRepository->getAccountCurrency($account);
|
||||||
$balance = $balances[$account->id]['balance'] ?? '0';
|
$balance = $balances[$account->id]['balance'] ?? '0';
|
||||||
|
|||||||
@@ -93,10 +93,10 @@ class IndexController extends Controller
|
|||||||
$start->subSecond();
|
$start->subSecond();
|
||||||
|
|
||||||
$ids = $accounts->pluck('id')->toArray();
|
$ids = $accounts->pluck('id')->toArray();
|
||||||
Log::debug(sprintf('inactive start: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('inactive start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||||
Log::debug(sprintf('inactive end: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('inactive end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$activities = Steam::getLastActivities($ids);
|
$activities = Steam::getLastActivities($ids);
|
||||||
|
|
||||||
|
|
||||||
@@ -170,10 +170,10 @@ class IndexController extends Controller
|
|||||||
$start->subSecond();
|
$start->subSecond();
|
||||||
|
|
||||||
$ids = $accounts->pluck('id')->toArray();
|
$ids = $accounts->pluck('id')->toArray();
|
||||||
Log::debug(sprintf('index start: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('index start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||||
Log::debug(sprintf('index end: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('index end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$activities = Steam::getLastActivities($ids);
|
$activities = Steam::getLastActivities($ids);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ use Illuminate\Routing\Redirector;
|
|||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ShowController
|
* Class ShowController
|
||||||
@@ -81,7 +82,9 @@ class ShowController extends Controller
|
|||||||
* */
|
* */
|
||||||
public function show(Request $request, Account $account, ?Carbon $start = null, ?Carbon $end = null)
|
public function show(Request $request, Account $account, ?Carbon $start = null, ?Carbon $end = null)
|
||||||
{
|
{
|
||||||
|
if (0 === $account->id) {
|
||||||
|
throw new NotFoundHttpException();
|
||||||
|
}
|
||||||
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
|
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
|
||||||
|
|
||||||
if (!$this->isEditableAccount($account)) {
|
if (!$this->isEditableAccount($account)) {
|
||||||
@@ -115,20 +118,27 @@ class ShowController extends Controller
|
|||||||
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||||
$firstTransaction = $this->repository->oldestJournalDate($account) ?? $start;
|
$firstTransaction = $this->repository->oldestJournalDate($account) ?? $start;
|
||||||
|
|
||||||
Log::debug('Start period overview');
|
// go back max 3 years.
|
||||||
Timer::start('period-overview');
|
$threeYearsAgo = clone $start;
|
||||||
|
$threeYearsAgo->startOfYear()->subYears(3);
|
||||||
|
if ($firstTransaction->lt($threeYearsAgo)) {
|
||||||
|
$firstTransaction = clone $threeYearsAgo;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::debug('Start period overview');
|
||||||
|
$timer = Timer::getInstance();
|
||||||
|
$timer->start('period-overview');
|
||||||
$periods = $this->getAccountPeriodOverview($account, $firstTransaction, $end);
|
$periods = $this->getAccountPeriodOverview($account, $firstTransaction, $end);
|
||||||
|
|
||||||
Log::debug('End period overview');
|
Log::debug('End period overview');
|
||||||
Timer::stop('period-overview');
|
$timer->stop('period-overview');
|
||||||
|
|
||||||
// if layout = v2, overrule the page title.
|
// if layout = v2, overrule the page title.
|
||||||
if ('v1' !== config('view.layout')) {
|
if ('v1' !== config('view.layout')) {
|
||||||
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
|
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
|
||||||
}
|
}
|
||||||
Log::debug('Collect transactions');
|
Log::debug('Collect transactions');
|
||||||
Timer::start('collection');
|
$timer->start('collection');
|
||||||
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
@@ -136,6 +146,7 @@ class ShowController extends Controller
|
|||||||
->setAccounts(new Collection([$account]))
|
->setAccounts(new Collection([$account]))
|
||||||
->setLimit($pageSize)
|
->setLimit($pageSize)
|
||||||
->setPage($page)
|
->setPage($page)
|
||||||
|
->withAttachmentInformation()
|
||||||
->withAPIInformation()
|
->withAPIInformation()
|
||||||
->setRange($start, $end)
|
->setRange($start, $end)
|
||||||
;
|
;
|
||||||
@@ -146,7 +157,7 @@ class ShowController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
Log::debug('End collect transactions');
|
Log::debug('End collect transactions');
|
||||||
Timer::stop('collection');
|
$timer->stop('collection');
|
||||||
|
|
||||||
// enrich data in arrays.
|
// enrich data in arrays.
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class IndexController extends Controller
|
|||||||
|
|
||||||
$this->middleware(
|
$this->middleware(
|
||||||
function ($request, $next) {
|
function ($request, $next) {
|
||||||
app('view')->share('title', (string) trans('firefly.bills'));
|
app('view')->share('title', (string)trans('firefly.bills'));
|
||||||
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
app('view')->share('mainTitleIcon', 'fa-calendar-o');
|
||||||
$this->repository = app(BillRepositoryInterface::class);
|
$this->repository = app(BillRepositoryInterface::class);
|
||||||
|
|
||||||
@@ -79,7 +79,6 @@ class IndexController extends Controller
|
|||||||
$total = $collection->count();
|
$total = $collection->count();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$parameters = new ParameterBag();
|
$parameters = new ParameterBag();
|
||||||
// sub one day from temp start so the last paid date is one day before it should be.
|
// sub one day from temp start so the last paid date is one day before it should be.
|
||||||
$tempStart = clone $start;
|
$tempStart = clone $start;
|
||||||
@@ -112,7 +111,7 @@ class IndexController extends Controller
|
|||||||
$bills = [
|
$bills = [
|
||||||
0 => [ // the index is the order, not the ID.
|
0 => [ // the index is the order, not the ID.
|
||||||
'object_group_id' => 0,
|
'object_group_id' => 0,
|
||||||
'object_group_title' => (string) trans('firefly.default_group_title_name'),
|
'object_group_title' => (string)trans('firefly.default_group_title_name'),
|
||||||
'bills' => [],
|
'bills' => [],
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
@@ -120,7 +119,7 @@ class IndexController extends Controller
|
|||||||
/** @var Bill $bill */
|
/** @var Bill $bill */
|
||||||
foreach ($collection as $bill) {
|
foreach ($collection as $bill) {
|
||||||
$array = $transformer->transform($bill);
|
$array = $transformer->transform($bill);
|
||||||
$groupOrder = (int) $array['object_group_order'];
|
$groupOrder = (int)$array['object_group_order'];
|
||||||
// make group array if necessary:
|
// make group array if necessary:
|
||||||
$bills[$groupOrder] ??= [
|
$bills[$groupOrder] ??= [
|
||||||
'object_group_id' => $array['object_group_id'],
|
'object_group_id' => $array['object_group_id'],
|
||||||
@@ -173,16 +172,28 @@ class IndexController extends Controller
|
|||||||
'currency_symbol' => $bill['currency_symbol'],
|
'currency_symbol' => $bill['currency_symbol'],
|
||||||
'currency_decimal_places' => $bill['currency_decimal_places'],
|
'currency_decimal_places' => $bill['currency_decimal_places'],
|
||||||
'avg' => '0',
|
'avg' => '0',
|
||||||
|
'total_left_to_pay' => '0',
|
||||||
'period' => $range,
|
'period' => $range,
|
||||||
'per_period' => '0',
|
'per_period' => '0',
|
||||||
];
|
];
|
||||||
|
|
||||||
// only fill in avg when bill is active.
|
// only fill in avg when bill is active.
|
||||||
if (null !== $bill['next_expected_match']) {
|
if (null !== $bill['next_expected_match']) {
|
||||||
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
|
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
|
||||||
$avg = bcmul($avg, (string) count($bill['pay_dates']));
|
$avg = bcmul($avg, (string)count($bill['pay_dates']));
|
||||||
$sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg);
|
$sums[$groupOrder][$currencyId]['avg'] = bcadd($sums[$groupOrder][$currencyId]['avg'], $avg);
|
||||||
}
|
}
|
||||||
|
// only fill in total_left_to_pay when bill is not yet paid.
|
||||||
|
if (count($bill['paid_dates']) < count($bill['pay_dates'])) {
|
||||||
|
$count = count($bill['pay_dates']) - count($bill['paid_dates']);
|
||||||
|
if ($count > 0) {
|
||||||
|
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
|
||||||
|
$avg = bcmul($avg, (string)$count);
|
||||||
|
$sums[$groupOrder][$currencyId]['total_left_to_pay'] = bcadd($sums[$groupOrder][$currencyId]['total_left_to_pay'], $avg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// fill in per period regardless:
|
// fill in per period regardless:
|
||||||
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
|
$sums[$groupOrder][$currencyId]['per_period'] = bcadd($sums[$groupOrder][$currencyId]['per_period'], $this->amountPerPeriod($bill, $range));
|
||||||
}
|
}
|
||||||
@@ -193,7 +204,7 @@ class IndexController extends Controller
|
|||||||
|
|
||||||
private function amountPerPeriod(array $bill, string $range): string
|
private function amountPerPeriod(array $bill, string $range): string
|
||||||
{
|
{
|
||||||
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
|
$avg = bcdiv(bcadd((string)$bill['amount_min'], (string)$bill['amount_max']), '2');
|
||||||
|
|
||||||
app('log')->debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name']));
|
app('log')->debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name']));
|
||||||
app('log')->debug(sprintf('Average is %s', $avg));
|
app('log')->debug(sprintf('Average is %s', $avg));
|
||||||
@@ -206,8 +217,8 @@ class IndexController extends Controller
|
|||||||
'weekly' => '52.17',
|
'weekly' => '52.17',
|
||||||
'daily' => '365.24',
|
'daily' => '365.24',
|
||||||
];
|
];
|
||||||
$yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
|
$yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1)));
|
||||||
app('log')->debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
|
app('log')->debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string)($bill['skip'] + 1)));
|
||||||
|
|
||||||
// per period:
|
// per period:
|
||||||
$division = [
|
$division = [
|
||||||
@@ -258,8 +269,8 @@ class IndexController extends Controller
|
|||||||
'period' => $entry['period'],
|
'period' => $entry['period'],
|
||||||
'per_period' => '0',
|
'per_period' => '0',
|
||||||
];
|
];
|
||||||
$totals[$currencyId]['avg'] = bcadd($totals[$currencyId]['avg'], (string) $entry['avg']);
|
$totals[$currencyId]['avg'] = bcadd($totals[$currencyId]['avg'], (string)$entry['avg']);
|
||||||
$totals[$currencyId]['per_period'] = bcadd($totals[$currencyId]['per_period'], (string) $entry['per_period']);
|
$totals[$currencyId]['per_period'] = bcadd($totals[$currencyId]['per_period'], (string)$entry['per_period']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,8 +282,8 @@ class IndexController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function setOrder(Request $request, Bill $bill): JsonResponse
|
public function setOrder(Request $request, Bill $bill): JsonResponse
|
||||||
{
|
{
|
||||||
$objectGroupTitle = (string) $request->get('objectGroupTitle');
|
$objectGroupTitle = (string)$request->get('objectGroupTitle');
|
||||||
$newOrder = (int) $request->get('order');
|
$newOrder = (int)$request->get('order');
|
||||||
$this->repository->setOrder($bill, $newOrder);
|
$this->repository->setOrder($bill, $newOrder);
|
||||||
if ('' !== $objectGroupTitle) {
|
if ('' !== $objectGroupTitle) {
|
||||||
$this->repository->setObjectGroup($bill, $objectGroupTitle);
|
$this->repository->setObjectGroup($bill, $objectGroupTitle);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use FireflyIII\Models\TransactionCurrency;
|
|||||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
use FireflyIII\Support\Facades\Steam;
|
use FireflyIII\Support\Facades\Steam;
|
||||||
use FireflyIII\Support\Http\Controllers\AugumentData;
|
use FireflyIII\Support\Http\Controllers\AugumentData;
|
||||||
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
use FireflyIII\Support\Http\Controllers\ChartGeneration;
|
||||||
@@ -78,6 +79,7 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Shows the balances for all the user's expense accounts (on the front page).
|
* Shows the balances for all the user's expense accounts (on the front page).
|
||||||
|
* 2025-08-06 validated for multi (primary) currency
|
||||||
*
|
*
|
||||||
* This chart is (multi) currency aware.
|
* This chart is (multi) currency aware.
|
||||||
*/
|
*/
|
||||||
@@ -112,11 +114,11 @@ class AccountController extends Controller
|
|||||||
$accountNames = $this->extractNames($accounts);
|
$accountNames = $this->extractNames($accounts);
|
||||||
|
|
||||||
// grab all balances
|
// grab all balances
|
||||||
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||||
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
|
Log::debug('Done collecting balances');
|
||||||
// loop the accounts, then check for balance and currency info.
|
// loop the accounts, then check for balance and currency info.
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
// Log::debug(sprintf('[a] Now in account #%d ("%s")', $account->id, $account->name));
|
// Log::debug(sprintf('[a] Now in account #%d ("%s")', $account->id, $account->name));
|
||||||
@@ -157,7 +159,7 @@ class AccountController extends Controller
|
|||||||
$tempData[] = [
|
$tempData[] = [
|
||||||
'name' => $accountNames[$account->id],
|
'name' => $accountNames[$account->id],
|
||||||
'difference' => $diff,
|
'difference' => $diff,
|
||||||
'diff_float' => (float) $diff, // intentional float
|
'diff_float' => (float)$diff, // intentional float
|
||||||
'currency_id' => $currencies[$searchCode]->id,
|
'currency_id' => $currencies[$searchCode]->id,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -182,7 +184,7 @@ class AccountController extends Controller
|
|||||||
foreach ($currencies as $currencyId => $currency) {
|
foreach ($currencies as $currencyId => $currency) {
|
||||||
$dataSet
|
$dataSet
|
||||||
= [
|
= [
|
||||||
'label' => (string) trans('firefly.spent'),
|
'label' => (string)trans('firefly.spent'),
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
@@ -195,7 +197,7 @@ class AccountController extends Controller
|
|||||||
foreach ($tempData as $entry) {
|
foreach ($tempData as $entry) {
|
||||||
$currencyId = $entry['currency_id'];
|
$currencyId = $entry['currency_id'];
|
||||||
$name = $entry['name'];
|
$name = $entry['name'];
|
||||||
$chartData[$currencyId]['entries'][$name] = (float) $entry['difference'];
|
$chartData[$currencyId]['entries'][$name] = (float)$entry['difference'];
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->generator->multiSet($chartData);
|
$data = $this->generator->multiSet($chartData);
|
||||||
@@ -223,6 +225,7 @@ class AccountController extends Controller
|
|||||||
$cache = new CacheProperties();
|
$cache = new CacheProperties();
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($this->convertToPrimary);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.expense-budget');
|
$cache->addProperty('chart.account.expense-budget');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
@@ -231,7 +234,10 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
/** @var GroupCollectorInterface $collector */
|
/** @var GroupCollectorInterface $collector */
|
||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setAccounts(new Collection([$account]))->setRange($start, $end)->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value]);
|
$collector->setAccounts(new Collection([$account]))
|
||||||
|
->setRange($start, $end)
|
||||||
|
->withBudgetInformation()->setTypes([TransactionTypeEnum::WITHDRAWAL->value])
|
||||||
|
;
|
||||||
$journals = $collector->getExtractedJournals();
|
$journals = $collector->getExtractedJournals();
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
$result = [];
|
$result = [];
|
||||||
@@ -239,19 +245,37 @@ class AccountController extends Controller
|
|||||||
|
|
||||||
/** @var array $journal */
|
/** @var array $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
$budgetId = (int) $journal['budget_id'];
|
$budgetId = (int)$journal['budget_id'];
|
||||||
$key = sprintf('%d-%d', $budgetId, $journal['currency_id']);
|
$key = sprintf('%d-%d', $budgetId, $journal['currency_id']);
|
||||||
$budgetIds[] = $budgetId;
|
$budgetIds[] = $budgetId;
|
||||||
|
|
||||||
|
// currency info:
|
||||||
|
$currencyId = (int)$journal['currency_id'];
|
||||||
|
$currencyName = $journal['currency_name'];
|
||||||
|
$currencySymbol = $journal['currency_symbol'];
|
||||||
|
$currencyCode = $journal['currency_code'];
|
||||||
|
$currencyDecimalPlaces = $journal['currency_decimal_places'];
|
||||||
|
$field = 'amount';
|
||||||
|
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
|
||||||
|
$field = 'pc_amount';
|
||||||
|
$currencyName = $this->primaryCurrency->name;
|
||||||
|
$currencySymbol = $this->primaryCurrency->symbol;
|
||||||
|
$currencyCode = $this->primaryCurrency->code;
|
||||||
|
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
if (!array_key_exists($key, $result)) {
|
if (!array_key_exists($key, $result)) {
|
||||||
$result[$key] = [
|
$result[$key] = [
|
||||||
'total' => '0',
|
'total' => '0',
|
||||||
'budget_id' => $budgetId,
|
'budget_id' => $budgetId,
|
||||||
'currency_name' => $journal['currency_name'],
|
'currency_name' => $currencyName,
|
||||||
'currency_symbol' => $journal['currency_symbol'],
|
'currency_symbol' => $currencySymbol,
|
||||||
'currency_code' => $journal['currency_code'],
|
'currency_code' => $currencyCode,
|
||||||
|
'currency_decimal_places' => $currencyDecimalPlaces,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']);
|
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$names = $this->getBudgetNames($budgetIds);
|
$names = $this->getBudgetNames($budgetIds);
|
||||||
@@ -259,7 +283,7 @@ class AccountController extends Controller
|
|||||||
foreach ($result as $row) {
|
foreach ($result as $row) {
|
||||||
$budgetId = $row['budget_id'];
|
$budgetId = $row['budget_id'];
|
||||||
$name = $names[$budgetId];
|
$name = $names[$budgetId];
|
||||||
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
|
$label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
|
||||||
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
|
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,6 +313,7 @@ class AccountController extends Controller
|
|||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
|
$cache->addProperty($this->convertToPrimary);
|
||||||
$cache->addProperty('chart.account.expense-category');
|
$cache->addProperty('chart.account.expense-category');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return response()->json($cache->get());
|
return response()->json($cache->get());
|
||||||
@@ -305,22 +330,39 @@ class AccountController extends Controller
|
|||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
|
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
|
||||||
if (!array_key_exists($key, $result)) {
|
if (!array_key_exists($key, $result)) {
|
||||||
$result[$key] = [
|
|
||||||
'total' => '0',
|
// currency info:
|
||||||
'category_id' => (int) $journal['category_id'],
|
$currencyId = (int)$journal['currency_id'];
|
||||||
'currency_name' => $journal['currency_name'],
|
$currencyName = $journal['currency_name'];
|
||||||
'currency_symbol' => $journal['currency_symbol'],
|
$currencySymbol = $journal['currency_symbol'];
|
||||||
'currency_code' => $journal['currency_code'],
|
$currencyCode = $journal['currency_code'];
|
||||||
|
$currencyDecimalPlaces = $journal['currency_decimal_places'];
|
||||||
|
$field = 'amount';
|
||||||
|
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
|
||||||
|
$field = 'pc_amount';
|
||||||
|
$currencyName = $this->primaryCurrency->name;
|
||||||
|
$currencySymbol = $this->primaryCurrency->symbol;
|
||||||
|
$currencyCode = $this->primaryCurrency->code;
|
||||||
|
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[$key] = [
|
||||||
|
'total' => '0',
|
||||||
|
'category_id' => (int)$journal['category_id'],
|
||||||
|
'currency_name' => $currencyName,
|
||||||
|
'currency_code' => $currencyCode,
|
||||||
|
'currency_symbol' => $currencySymbol,
|
||||||
|
'currency_decimal_places' => $currencyDecimalPlaces,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']);
|
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
|
||||||
}
|
}
|
||||||
$names = $this->getCategoryNames(array_keys($result));
|
$names = $this->getCategoryNames(array_keys($result));
|
||||||
|
|
||||||
foreach ($result as $row) {
|
foreach ($result as $row) {
|
||||||
$categoryId = $row['category_id'];
|
$categoryId = $row['category_id'];
|
||||||
$name = $names[$categoryId] ?? '(unknown)';
|
$name = $names[$categoryId] ?? '(unknown)';
|
||||||
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
|
$label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
|
||||||
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
|
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,11 +383,11 @@ class AccountController extends Controller
|
|||||||
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
|
$end = clone session('end', today(config('app.timezone'))->endOfMonth());
|
||||||
$defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
|
$defaultSet = $repository->getAccountsByType([AccountTypeEnum::DEFAULT->value, AccountTypeEnum::ASSET->value])->pluck('id')->toArray();
|
||||||
// Log::debug('Default set is ', $defaultSet);
|
// Log::debug('Default set is ', $defaultSet);
|
||||||
$frontpage = app('preferences')->get('frontpageAccounts', $defaultSet);
|
$frontpage = Preferences::get('frontpageAccounts', $defaultSet);
|
||||||
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
|
$frontpageArray = !is_array($frontpage->data) ? [] : $frontpage->data;
|
||||||
Log::debug('Frontpage preference set is ', $frontpageArray);
|
Log::debug('Frontpage preference set is ', $frontpageArray);
|
||||||
if (0 === count($frontpageArray)) {
|
if (0 === count($frontpageArray)) {
|
||||||
app('preferences')->set('frontpageAccounts', $defaultSet);
|
Preferences::set('frontpageAccounts', $defaultSet);
|
||||||
Log::debug('frontpage set is empty!');
|
Log::debug('frontpage set is empty!');
|
||||||
}
|
}
|
||||||
$accounts = $repository->getAccountsById($frontpageArray);
|
$accounts = $repository->getAccountsById($frontpageArray);
|
||||||
@@ -375,6 +417,7 @@ class AccountController extends Controller
|
|||||||
$cache = new CacheProperties();
|
$cache = new CacheProperties();
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
$cache->addProperty($start);
|
$cache->addProperty($start);
|
||||||
|
$cache->addProperty($this->convertToPrimary);
|
||||||
$cache->addProperty($end);
|
$cache->addProperty($end);
|
||||||
$cache->addProperty('chart.account.income-category');
|
$cache->addProperty('chart.account.income-category');
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
@@ -394,22 +437,39 @@ class AccountController extends Controller
|
|||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
|
$key = sprintf('%d-%d', $journal['category_id'], $journal['currency_id']);
|
||||||
if (!array_key_exists($key, $result)) {
|
if (!array_key_exists($key, $result)) {
|
||||||
$result[$key] = [
|
|
||||||
'total' => '0',
|
// currency info:
|
||||||
'category_id' => $journal['category_id'],
|
$currencyId = (int)$journal['currency_id'];
|
||||||
'currency_name' => $journal['currency_name'],
|
$currencyName = $journal['currency_name'];
|
||||||
'currency_symbol' => $journal['currency_symbol'],
|
$currencySymbol = $journal['currency_symbol'];
|
||||||
'currency_code' => $journal['currency_code'],
|
$currencyCode = $journal['currency_code'];
|
||||||
|
$currencyDecimalPlaces = $journal['currency_decimal_places'];
|
||||||
|
$field = 'amount';
|
||||||
|
if ($this->convertToPrimary && $this->primaryCurrency->id !== $currencyId) {
|
||||||
|
$field = 'pc_amount';
|
||||||
|
$currencyName = $this->primaryCurrency->name;
|
||||||
|
$currencySymbol = $this->primaryCurrency->symbol;
|
||||||
|
$currencyCode = $this->primaryCurrency->code;
|
||||||
|
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[$key] = [
|
||||||
|
'total' => '0',
|
||||||
|
'category_id' => $journal['category_id'],
|
||||||
|
'currency_name' => $currencyName,
|
||||||
|
'currency_code' => $currencyCode,
|
||||||
|
'currency_symbol' => $currencySymbol,
|
||||||
|
'currency_decimal_places' => $currencyDecimalPlaces,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
$result[$key]['total'] = bcadd((string) $journal['amount'], $result[$key]['total']);
|
$result[$key]['total'] = bcadd((string)$journal[$field], $result[$key]['total']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$names = $this->getCategoryNames(array_keys($result));
|
$names = $this->getCategoryNames(array_keys($result));
|
||||||
foreach ($result as $row) {
|
foreach ($result as $row) {
|
||||||
$categoryId = $row['category_id'];
|
$categoryId = $row['category_id'];
|
||||||
$name = $names[$categoryId] ?? '(unknown)';
|
$name = $names[$categoryId] ?? '(unknown)';
|
||||||
$label = (string) trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
|
$label = (string)trans('firefly.name_in_currency', ['name' => $name, 'currency' => $row['currency_name']]);
|
||||||
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
|
$chartData[$label] = ['amount' => $row['total'], 'currency_symbol' => $row['currency_symbol'], 'currency_code' => $row['currency_code']];
|
||||||
}
|
}
|
||||||
$data = $this->generator->multiCurrencyPieChart($chartData);
|
$data = $this->generator->multiCurrencyPieChart($chartData);
|
||||||
@@ -450,7 +510,7 @@ class AccountController extends Controller
|
|||||||
// This period depends on the size of the chart
|
// This period depends on the size of the chart
|
||||||
$current = clone $start;
|
$current = clone $start;
|
||||||
$current = app('navigation')->endOfX($current, $step, null);
|
$current = app('navigation')->endOfX($current, $step, null);
|
||||||
$format = (string) trans('config.month_and_day_js', [], $locale);
|
$format = (string)trans('config.month_and_day_js', [], $locale);
|
||||||
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
|
$accountCurrency = $this->accountRepository->getAccountCurrency($account);
|
||||||
|
|
||||||
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
|
$range = Steam::finalAccountBalanceInRange($account, $start, $end, $this->convertToPrimary);
|
||||||
@@ -512,7 +572,7 @@ class AccountController extends Controller
|
|||||||
foreach ($return as $key => $info) {
|
foreach ($return as $key => $info) {
|
||||||
if ('balance' !== $key && 'pc_balance' !== $key) {
|
if ('balance' !== $key && 'pc_balance' !== $key) {
|
||||||
// assume it's a currency:
|
// assume it's a currency:
|
||||||
$setCurrency = $this->currencyRepository->findByCode((string) $key);
|
$setCurrency = $this->currencyRepository->findByCode((string)$key);
|
||||||
$info['currency_symbol'] = $setCurrency->symbol;
|
$info['currency_symbol'] = $setCurrency->symbol;
|
||||||
$info['currency_code'] = $setCurrency->code;
|
$info['currency_code'] = $setCurrency->code;
|
||||||
$info['label'] = sprintf('%s (%s)', $account->name, $setCurrency->symbol);
|
$info['label'] = sprintf('%s (%s)', $account->name, $setCurrency->symbol);
|
||||||
@@ -525,7 +585,7 @@ class AccountController extends Controller
|
|||||||
if ('pc_balance' === $key) {
|
if ('pc_balance' === $key) {
|
||||||
$info['currency_symbol'] = $this->primaryCurrency->symbol;
|
$info['currency_symbol'] = $this->primaryCurrency->symbol;
|
||||||
$info['currency_code'] = $this->primaryCurrency->code;
|
$info['currency_code'] = $this->primaryCurrency->code;
|
||||||
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string) trans('firefly.sum'), $this->primaryCurrency->symbol);
|
$info['label'] = sprintf('%s (%s) (%s)', $account->name, (string)trans('firefly.sum'), $this->primaryCurrency->symbol);
|
||||||
}
|
}
|
||||||
$chartData[] = $info;
|
$chartData[] = $info;
|
||||||
}
|
}
|
||||||
@@ -594,10 +654,10 @@ class AccountController extends Controller
|
|||||||
$accountNames = $this->extractNames($accounts);
|
$accountNames = $this->extractNames($accounts);
|
||||||
|
|
||||||
// grab all balances
|
// grab all balances
|
||||||
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||||
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||||
|
|
||||||
|
|
||||||
// loop the accounts, then check for balance and currency info.
|
// loop the accounts, then check for balance and currency info.
|
||||||
@@ -640,7 +700,7 @@ class AccountController extends Controller
|
|||||||
$tempData[] = [
|
$tempData[] = [
|
||||||
'name' => $accountNames[$account->id],
|
'name' => $accountNames[$account->id],
|
||||||
'difference' => $diff,
|
'difference' => $diff,
|
||||||
'diff_float' => (float) $diff, // intentional float
|
'diff_float' => (float)$diff, // intentional float
|
||||||
'currency_id' => $currencies[$searchCode]->id,
|
'currency_id' => $currencies[$searchCode]->id,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -667,7 +727,7 @@ class AccountController extends Controller
|
|||||||
foreach ($currencies as $currencyId => $currency) {
|
foreach ($currencies as $currencyId => $currency) {
|
||||||
$dataSet
|
$dataSet
|
||||||
= [
|
= [
|
||||||
'label' => (string) trans('firefly.earned'),
|
'label' => (string)trans('firefly.earned'),
|
||||||
'type' => 'bar',
|
'type' => 'bar',
|
||||||
'currency_symbol' => $currency->symbol,
|
'currency_symbol' => $currency->symbol,
|
||||||
'currency_code' => $currency->code,
|
'currency_code' => $currency->code,
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
|
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Support\Facades\Amount;
|
use FireflyIII\Support\Facades\Amount;
|
||||||
use FireflyIII\Support\Facades\Steam;
|
use FireflyIII\Support\Facades\Steam;
|
||||||
@@ -33,6 +34,7 @@ use Illuminate\Foundation\Bus\DispatchesJobs;
|
|||||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||||
use Illuminate\Routing\Controller as BaseController;
|
use Illuminate\Routing\Controller as BaseController;
|
||||||
use Illuminate\Support\Facades\Config;
|
use Illuminate\Support\Facades\Config;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\View;
|
use Illuminate\Support\Facades\View;
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
@@ -141,6 +143,14 @@ abstract class Controller extends BaseController
|
|||||||
View::share('shownDemo', $shownDemo);
|
View::share('shownDemo', $shownDemo);
|
||||||
View::share('current_route_name', $page);
|
View::share('current_route_name', $page);
|
||||||
View::share('original_route_name', Route::currentRouteName());
|
View::share('original_route_name', Route::currentRouteName());
|
||||||
|
|
||||||
|
// lottery to send any remaining webhooks:
|
||||||
|
if (7 === random_int(1, 10)) {
|
||||||
|
// trigger event to send them:
|
||||||
|
Log::debug('send event RequestedSendWebhookMessages through lottery');
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
View::share('darkMode', $darkMode);
|
View::share('darkMode', $darkMode);
|
||||||
|
|
||||||
|
|||||||
@@ -179,14 +179,6 @@ class IndexController extends Controller
|
|||||||
$return[$accountId]['target'] = '0';
|
$return[$accountId]['target'] = '0';
|
||||||
$return[$accountId]['to_save'] = '0';
|
$return[$accountId]['to_save'] = '0';
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate new interesting fields:
|
|
||||||
// $return[$accountId]['left'] -= $array['current_amount'];
|
|
||||||
// $return[$accountId]['saved'] += $array['current_amount'];
|
|
||||||
// $return[$accountId]['target'] += $array['target_amount'];
|
|
||||||
// $return[$accountId]['to_save'] += ($array['target_amount'] - $array['current_amount']);
|
|
||||||
// $return['account_name'] = $account['name'];
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Controllers;
|
namespace FireflyIII\Http\Controllers;
|
||||||
|
|
||||||
|
use FireflyIII\Support\Singleton\PreferencesSingleton;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Enums\AccountTypeEnum;
|
use FireflyIII\Enums\AccountTypeEnum;
|
||||||
@@ -269,7 +270,9 @@ class PreferencesController extends Controller
|
|||||||
if ($convertToPrimary && !$this->convertToPrimary) {
|
if ($convertToPrimary && !$this->convertToPrimary) {
|
||||||
// set to true!
|
// set to true!
|
||||||
Log::debug('User sets convertToPrimary to true.');
|
Log::debug('User sets convertToPrimary to true.');
|
||||||
Preferences::set('convert_to_primary', $convertToPrimary);
|
Preferences::set('convert_to_primary', true);
|
||||||
|
$singleton = PreferencesSingleton::getInstance();
|
||||||
|
$singleton->resetPreferences();
|
||||||
event(new UserGroupChangedPrimaryCurrency(auth()->user()->userGroup));
|
event(new UserGroupChangedPrimaryCurrency(auth()->user()->userGroup));
|
||||||
}
|
}
|
||||||
Preferences::set('convert_to_primary', $convertToPrimary);
|
Preferences::set('convert_to_primary', $convertToPrimary);
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ use FireflyIII\Models\RecurrenceRepetition;
|
|||||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
use FireflyIII\Repositories\Budget\BudgetRepositoryInterface;
|
||||||
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface;
|
||||||
|
use FireflyIII\Support\Facades\ExpandedForm;
|
||||||
use FireflyIII\Support\JsonApi\Enrichments\RecurringEnrichment;
|
use FireflyIII\Support\JsonApi\Enrichments\RecurringEnrichment;
|
||||||
use FireflyIII\Transformers\RecurrenceTransformer;
|
use FireflyIII\Transformers\RecurrenceTransformer;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
@@ -106,8 +107,8 @@ class EditController extends Controller
|
|||||||
$transformer->setParameters(new ParameterBag());
|
$transformer->setParameters(new ParameterBag());
|
||||||
|
|
||||||
$array = $transformer->transform($recurrence);
|
$array = $transformer->transform($recurrence);
|
||||||
$budgets = app('expandedform')->makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
|
$budgets = ExpandedForm::makeSelectListWithEmpty($this->budgetRepos->getActiveBudgets());
|
||||||
$bills = app('expandedform')->makeSelectListWithEmpty($this->billRepository->getActiveBills());
|
$bills = ExpandedForm::makeSelectListWithEmpty($this->billRepository->getActiveBills());
|
||||||
|
|
||||||
/** @var RecurrenceRepetition $repetition */
|
/** @var RecurrenceRepetition $repetition */
|
||||||
$repetition = $recurrence->recurrenceRepetitions()->first();
|
$repetition = $recurrence->recurrenceRepetitions()->first();
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Http\Controllers\Report;
|
namespace FireflyIII\Http\Controllers\Report;
|
||||||
|
|
||||||
|
use FireflyIII\Support\Facades\Navigation;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
@@ -290,11 +291,12 @@ class BudgetController extends Controller
|
|||||||
$cache->addProperty('budget-period-report');
|
$cache->addProperty('budget-period-report');
|
||||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
return $cache->get();
|
// return $cache->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
$periods = app('navigation')->listOfPeriods($start, $end);
|
$periods = Navigation::listOfPeriods($start, $end);
|
||||||
$keyFormat = app('navigation')->preferredCarbonFormat($start, $end);
|
$keyFormat = Navigation::preferredCarbonFormat($start, $end);
|
||||||
|
|
||||||
// list expenses for budgets in account(s)
|
// list expenses for budgets in account(s)
|
||||||
$expenses = $this->opsRepository->listExpenses($start, $end, $accounts);
|
$expenses = $this->opsRepository->listExpenses($start, $end, $accounts);
|
||||||
|
|
||||||
@@ -303,6 +305,17 @@ class BudgetController extends Controller
|
|||||||
foreach ($currency['budgets'] as $budget) {
|
foreach ($currency['budgets'] as $budget) {
|
||||||
$count = 0;
|
$count = 0;
|
||||||
foreach ($budget['transaction_journals'] as $journal) {
|
foreach ($budget['transaction_journals'] as $journal) {
|
||||||
|
// #10678
|
||||||
|
// skip transactions between two asset / liability accounts.
|
||||||
|
if (
|
||||||
|
in_array($journal['source_account_type'], config('firefly.valid_currency_account_types'), true)
|
||||||
|
&& in_array($journal['destination_account_type'], config('firefly.valid_currency_account_types'), true)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
++$count;
|
++$count;
|
||||||
$key = sprintf('%d-%d', $budget['id'], $currency['currency_id']);
|
$key = sprintf('%d-%d', $budget['id'], $currency['currency_id']);
|
||||||
$dateKey = $journal['date']->format($keyFormat);
|
$dateKey = $journal['date']->format($keyFormat);
|
||||||
|
|||||||
@@ -188,16 +188,7 @@ class ReportController extends Controller
|
|||||||
$start->endOfDay(); // end of day so the final balance is at the end of that day.
|
$start->endOfDay(); // end of day so the final balance is at the end of that day.
|
||||||
$end->endOfDay();
|
$end->endOfDay();
|
||||||
|
|
||||||
app('view')->share(
|
app('view')->share('subTitle', trans('firefly.report_default', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]));
|
||||||
'subTitle',
|
|
||||||
trans(
|
|
||||||
'firefly.report_default',
|
|
||||||
[
|
|
||||||
'start' => $start->isoFormat($this->monthAndDayFormat),
|
|
||||||
'end' => $end->isoFormat($this->monthAndDayFormat),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
|
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
|
||||||
$generator->setAccounts($accounts);
|
$generator->setAccounts($accounts);
|
||||||
@@ -222,16 +213,7 @@ class ReportController extends Controller
|
|||||||
$start->endOfDay(); // end of day so the final balance is at the end of that day.
|
$start->endOfDay(); // end of day so the final balance is at the end of that day.
|
||||||
$end->endOfDay();
|
$end->endOfDay();
|
||||||
|
|
||||||
app('view')->share(
|
app('view')->share('subTitle', trans('firefly.report_double', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]));
|
||||||
'subTitle',
|
|
||||||
trans(
|
|
||||||
'firefly.report_double',
|
|
||||||
[
|
|
||||||
'start' => $start->isoFormat($this->monthAndDayFormat),
|
|
||||||
'end' => $end->isoFormat($this->monthAndDayFormat),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$generator = ReportGeneratorFactory::reportGenerator('Account', $start, $end);
|
$generator = ReportGeneratorFactory::reportGenerator('Account', $start, $end);
|
||||||
$generator->setAccounts($accounts);
|
$generator->setAccounts($accounts);
|
||||||
|
|||||||
@@ -270,6 +270,7 @@ class CreateController extends Controller
|
|||||||
$data = $request->getRuleData();
|
$data = $request->getRuleData();
|
||||||
|
|
||||||
$rule = $this->ruleRepos->store($data);
|
$rule = $this->ruleRepos->store($data);
|
||||||
|
session()->flash('success_url', route('rules.select-transactions', [$rule->id]));
|
||||||
session()->flash('success', (string) trans('firefly.stored_new_rule', ['title' => $rule->title]));
|
session()->flash('success', (string) trans('firefly.stored_new_rule', ['title' => $rule->title]));
|
||||||
app('preferences')->mark();
|
app('preferences')->mark();
|
||||||
|
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ class TagController extends Controller
|
|||||||
|
|
||||||
$collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()
|
$collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()
|
||||||
->setTag($tag)->withBudgetInformation()->withCategoryInformation()
|
->setTag($tag)->withBudgetInformation()->withCategoryInformation()
|
||||||
|
->withAttachmentInformation()
|
||||||
;
|
;
|
||||||
$groups = $collector->getPaginatedGroups();
|
$groups = $collector->getPaginatedGroups();
|
||||||
$groups->setPath($path);
|
$groups->setPath($path);
|
||||||
@@ -283,6 +284,7 @@ class TagController extends Controller
|
|||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
$collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()
|
$collector->setRange($start, $end)->setLimit($pageSize)->setPage($page)->withAccountInformation()
|
||||||
->setTag($tag)->withBudgetInformation()->withCategoryInformation()
|
->setTag($tag)->withBudgetInformation()->withCategoryInformation()
|
||||||
|
->withAttachmentInformation()
|
||||||
;
|
;
|
||||||
$groups = $collector->getPaginatedGroups();
|
$groups = $collector->getPaginatedGroups();
|
||||||
$groups->setPath($path);
|
$groups->setPath($path);
|
||||||
|
|||||||
@@ -25,16 +25,20 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Http\Controllers\Transaction;
|
namespace FireflyIII\Http\Controllers\Transaction;
|
||||||
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Models\TransactionGroup;
|
use FireflyIII\Models\TransactionGroup;
|
||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface;
|
use FireflyIII\Repositories\AuditLogEntry\ALERepositoryInterface;
|
||||||
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
use FireflyIII\Repositories\TransactionGroup\TransactionGroupRepositoryInterface;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\TransactionGroupEnrichment;
|
||||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\View\View;
|
use Illuminate\View\View;
|
||||||
use Symfony\Component\HttpFoundation\ParameterBag;
|
use Symfony\Component\HttpFoundation\ParameterBag;
|
||||||
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ShowController
|
* Class ShowController
|
||||||
@@ -57,7 +61,7 @@ class ShowController extends Controller
|
|||||||
$this->repository = app(TransactionGroupRepositoryInterface::class);
|
$this->repository = app(TransactionGroupRepositoryInterface::class);
|
||||||
$this->aleRepository = app(ALERepositoryInterface::class);
|
$this->aleRepository = app(ALERepositoryInterface::class);
|
||||||
|
|
||||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||||
|
|
||||||
return $next($request);
|
return $next($request);
|
||||||
@@ -80,38 +84,62 @@ class ShowController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function show(TransactionGroup $transactionGroup)
|
public function show(TransactionGroup $transactionGroup)
|
||||||
{
|
{
|
||||||
|
/** @var User $admin */
|
||||||
|
$admin = auth()->user();
|
||||||
|
|
||||||
|
// use new group collector:
|
||||||
|
/** @var GroupCollectorInterface $collector */
|
||||||
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
$collector->setUser($admin)->setTransactionGroup($transactionGroup)->withAPIInformation();
|
||||||
|
|
||||||
|
$selectedGroup = $collector->getGroups()->first();
|
||||||
|
if (null === $selectedGroup) {
|
||||||
|
throw new NotFoundHttpException();
|
||||||
|
}
|
||||||
|
|
||||||
|
// enrich
|
||||||
|
$enrichment = new TransactionGroupEnrichment();
|
||||||
|
$enrichment->setUser($admin);
|
||||||
|
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
|
||||||
|
|
||||||
|
|
||||||
/** @var null|TransactionJournal $first */
|
/** @var null|TransactionJournal $first */
|
||||||
$first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']);
|
$first = $transactionGroup->transactionJournals()->first(['transaction_journals.*']);
|
||||||
$splits = $transactionGroup->transactionJournals()->count();
|
$splits = $transactionGroup->transactionJournals()->count();
|
||||||
|
$splits = count($selectedGroup['transactions']);
|
||||||
|
$keys = array_keys($selectedGroup['transactions']);
|
||||||
|
$first = $selectedGroup['transactions'][array_shift($keys)];
|
||||||
|
unset($keys);
|
||||||
|
|
||||||
if (null === $first) {
|
if (null === $first) {
|
||||||
throw new FireflyException('This transaction is broken :(.');
|
throw new FireflyException('This transaction is broken :(.');
|
||||||
}
|
}
|
||||||
|
$type = (string)trans(sprintf('firefly.%s', $first['transaction_type_type']));
|
||||||
$type = (string) trans(sprintf('firefly.%s', $first->transactionType->type));
|
$title = 1 === $splits ? $first['description'] : $selectedGroup['title'];
|
||||||
$title = 1 === $splits ? $first->description : $transactionGroup->title;
|
|
||||||
$subTitle = sprintf('%s: "%s"', $type, $title);
|
$subTitle = sprintf('%s: "%s"', $type, $title);
|
||||||
|
|
||||||
|
// enrich
|
||||||
|
$enrichment = new TransactionGroupEnrichment();
|
||||||
|
$enrichment->setUser($admin);
|
||||||
|
$selectedGroup = $enrichment->enrichSingle($selectedGroup);
|
||||||
|
|
||||||
/** @var TransactionGroupTransformer $transformer */
|
/** @var TransactionGroupTransformer $transformer */
|
||||||
$transformer = app(TransactionGroupTransformer::class);
|
$transformer = app(TransactionGroupTransformer::class);
|
||||||
$transformer->setParameters(new ParameterBag());
|
$transformer->setParameters(new ParameterBag());
|
||||||
$groupArray = $transformer->transformObject($transactionGroup);
|
$groupArray = $transformer->transformObject($transactionGroup);
|
||||||
|
|
||||||
// do some calculations:
|
// do some calculations:
|
||||||
$amounts = $this->getAmounts($groupArray);
|
$amounts = $this->getAmounts($selectedGroup);
|
||||||
$accounts = $this->getAccounts($groupArray);
|
$accounts = $this->getAccounts($selectedGroup);
|
||||||
|
|
||||||
foreach (array_keys($groupArray['transactions']) as $index) {
|
foreach (array_keys($selectedGroup['transactions']) as $index) {
|
||||||
$groupArray['transactions'][$index]['tags'] = $this->repository->getTagObjects(
|
$selectedGroup['transactions'][$index]['tags'] = $this->repository->getTagObjects((int)$selectedGroup['transactions'][$index]['transaction_journal_id']);
|
||||||
(int) $groupArray['transactions'][$index]['transaction_journal_id']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get audit log entries:
|
// get audit log entries:
|
||||||
$groupLogEntries = $this->aleRepository->getForObject($transactionGroup);
|
$groupLogEntries = $this->aleRepository->getForObject($transactionGroup);
|
||||||
$logEntries = [];
|
$logEntries = [];
|
||||||
foreach ($transactionGroup->transactionJournals as $journal) {
|
foreach ($selectedGroup['transactions'] as $journal) {
|
||||||
$logEntries[$journal->id] = $this->aleRepository->getForObject($journal);
|
$logEntries[$journal['transaction_journal_id']] = $this->aleRepository->getForId(TransactionJournal::class, $journal['transaction_journal_id']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$events = $this->repository->getPiggyEvents($transactionGroup);
|
$events = $this->repository->getPiggyEvents($transactionGroup);
|
||||||
@@ -129,6 +157,7 @@ class ShowController extends Controller
|
|||||||
'groupLogEntries',
|
'groupLogEntries',
|
||||||
'subTitle',
|
'subTitle',
|
||||||
'splits',
|
'splits',
|
||||||
|
'selectedGroup',
|
||||||
'groupArray',
|
'groupArray',
|
||||||
'events',
|
'events',
|
||||||
'attachments',
|
'attachments',
|
||||||
@@ -142,34 +171,38 @@ class ShowController extends Controller
|
|||||||
{
|
{
|
||||||
$amounts = [];
|
$amounts = [];
|
||||||
foreach ($group['transactions'] as $transaction) {
|
foreach ($group['transactions'] as $transaction) {
|
||||||
|
// add normal amount:
|
||||||
$symbol = $transaction['currency_symbol'];
|
$symbol = $transaction['currency_symbol'];
|
||||||
if (!array_key_exists($symbol, $amounts)) {
|
$amounts[$symbol] ??= [
|
||||||
$amounts[$symbol] = [
|
'amount' => '0',
|
||||||
'amount' => '0',
|
'symbol' => $symbol,
|
||||||
'symbol' => $symbol,
|
'decimal_places' => $transaction['currency_decimal_places'],
|
||||||
'decimal_places' => $transaction['currency_decimal_places'],
|
];
|
||||||
];
|
$amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string)$transaction['amount']);
|
||||||
}
|
|
||||||
$amounts[$symbol]['amount'] = bcadd($amounts[$symbol]['amount'], (string) $transaction['amount']);
|
// add foreign amount:
|
||||||
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount']
|
if (null !== $transaction['foreign_amount'] && '' !== $transaction['foreign_amount'] && 0 !== bccomp('0', (string)$transaction['foreign_amount'])) {
|
||||||
&& 0 !== bccomp(
|
|
||||||
'0',
|
|
||||||
(string) $transaction['foreign_amount']
|
|
||||||
)) {
|
|
||||||
// same for foreign currency:
|
// same for foreign currency:
|
||||||
$foreignSymbol = $transaction['foreign_currency_symbol'];
|
$foreignSymbol = $transaction['foreign_currency_symbol'];
|
||||||
if (!array_key_exists($foreignSymbol, $amounts)) {
|
$amounts[$foreignSymbol] ??= [
|
||||||
$amounts[$foreignSymbol] = [
|
'amount' => '0',
|
||||||
'amount' => '0',
|
'symbol' => $foreignSymbol,
|
||||||
'symbol' => $foreignSymbol,
|
'decimal_places' => $transaction['foreign_currency_decimal_places'],
|
||||||
'decimal_places' => $transaction['foreign_currency_decimal_places'],
|
];
|
||||||
];
|
$amounts[$foreignSymbol]['amount'] = bcadd($amounts[$foreignSymbol]['amount'], (string)$transaction['foreign_amount']);
|
||||||
}
|
|
||||||
$amounts[$foreignSymbol]['amount'] = bcadd(
|
|
||||||
$amounts[$foreignSymbol]['amount'],
|
|
||||||
(string) $transaction['foreign_amount']
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
// add primary currency amount
|
||||||
|
if (null !== $transaction['pc_amount'] && $transaction['currency_id'] !== $this->primaryCurrency->id) {
|
||||||
|
// same for foreign currency:
|
||||||
|
$primarySymbol = $this->primaryCurrency->symbol;
|
||||||
|
$amounts[$primarySymbol] ??= [
|
||||||
|
'amount' => '0',
|
||||||
|
'symbol' => $this->primaryCurrency->symbol,
|
||||||
|
'decimal_places' => $this->primaryCurrency->decimal_places,
|
||||||
|
];
|
||||||
|
$amounts[$primarySymbol]['amount'] = bcadd($amounts[$primarySymbol]['amount'], (string)$transaction['pc_amount']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $amounts;
|
return $amounts;
|
||||||
@@ -184,16 +217,16 @@ class ShowController extends Controller
|
|||||||
|
|
||||||
foreach ($group['transactions'] as $transaction) {
|
foreach ($group['transactions'] as $transaction) {
|
||||||
$accounts['source'][] = [
|
$accounts['source'][] = [
|
||||||
'type' => $transaction['source_type'],
|
'type' => $transaction['source_account_type'],
|
||||||
'id' => $transaction['source_id'],
|
'id' => $transaction['source_account_id'],
|
||||||
'name' => $transaction['source_name'],
|
'name' => $transaction['source_account_name'],
|
||||||
'iban' => $transaction['source_iban'],
|
'iban' => $transaction['source_account_iban'],
|
||||||
];
|
];
|
||||||
$accounts['destination'][] = [
|
$accounts['destination'][] = [
|
||||||
'type' => $transaction['destination_type'],
|
'type' => $transaction['destination_account_type'],
|
||||||
'id' => $transaction['destination_id'],
|
'id' => $transaction['destination_account_id'],
|
||||||
'name' => $transaction['destination_name'],
|
'name' => $transaction['destination_account_name'],
|
||||||
'iban' => $transaction['destination_iban'],
|
'iban' => $transaction['destination_account_iban'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,13 +25,18 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Jobs;
|
namespace FireflyIII\Jobs;
|
||||||
|
|
||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Events\WarnUserAboutBill;
|
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
||||||
|
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
||||||
use FireflyIII\Models\Bill;
|
use FireflyIII\Models\Bill;
|
||||||
|
use FireflyIII\Support\Facades\Navigation;
|
||||||
|
use FireflyIII\Support\JsonApi\Enrichments\SubscriptionEnrichment;
|
||||||
|
use FireflyIII\User;
|
||||||
use Illuminate\Bus\Queueable;
|
use Illuminate\Bus\Queueable;
|
||||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
use Illuminate\Foundation\Bus\Dispatchable;
|
use Illuminate\Foundation\Bus\Dispatchable;
|
||||||
use Illuminate\Queue\InteractsWithQueue;
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class WarnAboutBills
|
* Class WarnAboutBills
|
||||||
@@ -63,7 +68,7 @@ class WarnAboutBills implements ShouldQueue
|
|||||||
|
|
||||||
$this->force = false;
|
$this->force = false;
|
||||||
|
|
||||||
app('log')->debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d')));
|
Log::debug(sprintf('Created new WarnAboutBills("%s")', $this->date->format('Y-m-d')));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -71,36 +76,42 @@ class WarnAboutBills implements ShouldQueue
|
|||||||
*/
|
*/
|
||||||
public function handle(): void
|
public function handle(): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y')));
|
Log::debug(sprintf('Now at start of WarnAboutBills() job for %s.', $this->date->format('D d M Y')));
|
||||||
$bills = Bill::all();
|
foreach (User::all() as $user) {
|
||||||
|
$bills = $user->bills()->where('active', true)->get();
|
||||||
|
$overdue = [];
|
||||||
|
|
||||||
/** @var Bill $bill */
|
/** @var Bill $bill */
|
||||||
foreach ($bills as $bill) {
|
foreach ($bills as $bill) {
|
||||||
app('log')->debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name));
|
Log::debug(sprintf('Now checking bill #%d ("%s")', $bill->id, $bill->name));
|
||||||
if ($this->hasDateFields($bill)) {
|
$dates = $this->getDates($bill);
|
||||||
if ($this->needsWarning($bill, 'end_date')) {
|
if ($this->needsOverdueAlert($dates)) {
|
||||||
$this->sendWarning($bill, 'end_date');
|
$overdue[] = ['bill' => $bill, 'dates' => $dates];
|
||||||
}
|
}
|
||||||
if ($this->needsWarning($bill, 'extension_date')) {
|
if ($this->hasDateFields($bill)) {
|
||||||
$this->sendWarning($bill, 'extension_date');
|
if ($this->needsWarning($bill, 'end_date')) {
|
||||||
|
$this->sendWarning($bill, 'end_date');
|
||||||
|
}
|
||||||
|
if ($this->needsWarning($bill, 'extension_date')) {
|
||||||
|
$this->sendWarning($bill, 'extension_date');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$this->sendOverdueAlerts($user, $overdue);
|
||||||
}
|
}
|
||||||
app('log')->debug('Done with handle()');
|
Log::debug('Done with handle()');
|
||||||
|
|
||||||
// clear cache:
|
|
||||||
app('preferences')->mark();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function hasDateFields(Bill $bill): bool
|
private function hasDateFields(Bill $bill): bool
|
||||||
{
|
{
|
||||||
if (false === $bill->active) {
|
if (false === $bill->active) {
|
||||||
app('log')->debug('Bill is not active.');
|
Log::debug('Bill is not active.');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (null === $bill->end_date && null === $bill->extension_date) {
|
if (null === $bill->end_date && null === $bill->extension_date) {
|
||||||
app('log')->debug('Bill has no date fields.');
|
Log::debug('Bill has no date fields.');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -115,7 +126,7 @@ class WarnAboutBills implements ShouldQueue
|
|||||||
}
|
}
|
||||||
$diff = $this->getDiff($bill, $field);
|
$diff = $this->getDiff($bill, $field);
|
||||||
$list = config('firefly.bill_reminder_periods');
|
$list = config('firefly.bill_reminder_periods');
|
||||||
app('log')->debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->{$field}->format('Y-m-d'), $diff));
|
Log::debug(sprintf('Difference in days for field "%s" ("%s") is %d day(s)', $field, $bill->{$field}->format('Y-m-d'), $diff));
|
||||||
if (in_array($diff, $list, true)) {
|
if (in_array($diff, $list, true)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -128,13 +139,13 @@ class WarnAboutBills implements ShouldQueue
|
|||||||
$today = clone $this->date;
|
$today = clone $this->date;
|
||||||
$carbon = clone $bill->{$field};
|
$carbon = clone $bill->{$field};
|
||||||
|
|
||||||
return (int) $today->diffInDays($carbon);
|
return (int)$today->diffInDays($carbon);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sendWarning(Bill $bill, string $field): void
|
private function sendWarning(Bill $bill, string $field): void
|
||||||
{
|
{
|
||||||
$diff = $this->getDiff($bill, $field);
|
$diff = $this->getDiff($bill, $field);
|
||||||
app('log')->debug('Will now send warning!');
|
Log::debug('Will now send warning!');
|
||||||
event(new WarnUserAboutBill($bill, $field, $diff));
|
event(new WarnUserAboutBill($bill, $field, $diff));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,4 +160,49 @@ class WarnAboutBills implements ShouldQueue
|
|||||||
{
|
{
|
||||||
$this->force = $force;
|
$this->force = $force;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getDates(Bill $bill): array
|
||||||
|
{
|
||||||
|
$start = clone $this->date;
|
||||||
|
$start = Navigation::startOfPeriod($start, $bill->repeat_freq);
|
||||||
|
$end = clone $start;
|
||||||
|
$end = Navigation::endOfPeriod($end, $bill->repeat_freq);
|
||||||
|
$enrichment = new SubscriptionEnrichment();
|
||||||
|
$enrichment->setUser($bill->user);
|
||||||
|
$enrichment->setStart($start);
|
||||||
|
$enrichment->setEnd($end);
|
||||||
|
$single = $enrichment->enrichSingle($bill);
|
||||||
|
|
||||||
|
return [
|
||||||
|
'pay_dates' => $single->meta['pay_dates'] ?? [],
|
||||||
|
'paid_dates' => $single->meta['paid_dates'] ?? [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
private function needsOverdueAlert(array $dates): bool
|
||||||
|
{
|
||||||
|
$count = count($dates['pay_dates']) - count($dates['paid_dates']);
|
||||||
|
if (0 === $count || 0 === count($dates['pay_dates'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// the earliest date in the list of pay dates must be 48hrs or more ago.
|
||||||
|
$earliest = new Carbon($dates['pay_dates'][0]);
|
||||||
|
$earliest->startOfDay();
|
||||||
|
Log::debug(sprintf('Earliest expected pay date is %s', $earliest->toAtomString()));
|
||||||
|
$diff = $earliest->diffInDays($this->date);
|
||||||
|
Log::debug(sprintf('Difference in days is %s', $diff));
|
||||||
|
if ($diff < 2) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sendOverdueAlerts(User $user, array $overdue): void
|
||||||
|
{
|
||||||
|
if (count($overdue) > 0) {
|
||||||
|
Log::debug(sprintf('Will now send warning about overdue bill for user #%d.', $user->id));
|
||||||
|
event(new WarnUserAboutOverdueSubscriptions($user, $overdue));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
113
app/Notifications/User/SubscriptionsOverdueReminder.php
Normal file
113
app/Notifications/User/SubscriptionsOverdueReminder.php
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Notifications\User;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Notifications\ReturnsAvailableChannels;
|
||||||
|
use FireflyIII\Notifications\ReturnsSettings;
|
||||||
|
use FireflyIII\User;
|
||||||
|
use Illuminate\Bus\Queueable;
|
||||||
|
use Illuminate\Notifications\Messages\MailMessage;
|
||||||
|
use Illuminate\Notifications\Messages\SlackMessage;
|
||||||
|
use Illuminate\Notifications\Notification;
|
||||||
|
use NotificationChannels\Pushover\PushoverMessage;
|
||||||
|
|
||||||
|
class SubscriptionsOverdueReminder extends Notification
|
||||||
|
{
|
||||||
|
use Queueable;
|
||||||
|
|
||||||
|
public function __construct(private array $overdue) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||||
|
*/
|
||||||
|
public function toArray(User $notifiable): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||||
|
*/
|
||||||
|
public function toMail(User $notifiable): MailMessage
|
||||||
|
{
|
||||||
|
// format the data
|
||||||
|
$info = [];
|
||||||
|
$count = 0;
|
||||||
|
foreach ($this->overdue as $item) {
|
||||||
|
$current = [
|
||||||
|
'bill' => $item['bill'],
|
||||||
|
];
|
||||||
|
$current['pay_dates'] = array_map(
|
||||||
|
static function (string $date): string {
|
||||||
|
return new Carbon($date)->isoFormat((string)trans('config.month_and_day_moment_js'));
|
||||||
|
},
|
||||||
|
$item['dates']['pay_dates']
|
||||||
|
);
|
||||||
|
$info[] = $current;
|
||||||
|
++$count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new MailMessage()
|
||||||
|
->markdown('emails.subscriptions-overdue-warning', ['info' => $info, 'count' => $count])
|
||||||
|
->subject($this->getSubject())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getSubject(): string
|
||||||
|
{
|
||||||
|
if (count($this->overdue) > 1) {
|
||||||
|
return (string)trans('email.subscriptions_overdue_subject_multi', ['count' => count($this->overdue)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (string)trans('email.subscriptions_overdue_subject_single');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toNtfy(User $notifiable): Message
|
||||||
|
{
|
||||||
|
$settings = ReturnsSettings::getSettings('ntfy', 'user', $notifiable);
|
||||||
|
$message = new Message();
|
||||||
|
$message->topic($settings['ntfy_topic']);
|
||||||
|
$message->title($this->getSubject());
|
||||||
|
$message->body((string)trans('email.bill_warning_please_action'));
|
||||||
|
|
||||||
|
return $message;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||||
|
*/
|
||||||
|
public function toPushover(User $notifiable): PushoverMessage
|
||||||
|
{
|
||||||
|
return PushoverMessage::create((string)trans('email.bill_warning_please_action'))
|
||||||
|
->title($this->getSubject())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||||
|
*/
|
||||||
|
public function toSlack(User $notifiable): SlackMessage
|
||||||
|
{
|
||||||
|
$url = route('bills.index');
|
||||||
|
|
||||||
|
return new SlackMessage()
|
||||||
|
->warning()
|
||||||
|
->attachment(static function ($attachment) use ($url): void {
|
||||||
|
$attachment->title((string)trans('firefly.visit_bills'), $url);
|
||||||
|
})
|
||||||
|
->content($this->getSubject())
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||||
|
*/
|
||||||
|
public function via(User $notifiable): array
|
||||||
|
{
|
||||||
|
return ReturnsAvailableChannels::returnChannels('user', $notifiable);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,6 +27,8 @@ use FireflyIII\Events\ActuallyLoggedIn;
|
|||||||
use FireflyIII\Events\Admin\InvitationCreated;
|
use FireflyIII\Events\Admin\InvitationCreated;
|
||||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||||
use FireflyIII\Events\DetectedNewIPAddress;
|
use FireflyIII\Events\DetectedNewIPAddress;
|
||||||
|
use FireflyIII\Events\Model\Bill\WarnUserAboutBill;
|
||||||
|
use FireflyIII\Events\Model\Bill\WarnUserAboutOverdueSubscriptions;
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Created;
|
use FireflyIII\Events\Model\BudgetLimit\Created;
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
use FireflyIII\Events\Model\BudgetLimit\Deleted;
|
||||||
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
use FireflyIII\Events\Model\BudgetLimit\Updated;
|
||||||
@@ -58,7 +60,6 @@ use FireflyIII\Events\TriggeredAuditLog;
|
|||||||
use FireflyIII\Events\UpdatedAccount;
|
use FireflyIII\Events\UpdatedAccount;
|
||||||
use FireflyIII\Events\UpdatedTransactionGroup;
|
use FireflyIII\Events\UpdatedTransactionGroup;
|
||||||
use FireflyIII\Events\UserChangedEmail;
|
use FireflyIII\Events\UserChangedEmail;
|
||||||
use FireflyIII\Events\WarnUserAboutBill;
|
|
||||||
use FireflyIII\Handlers\Observer\AccountObserver;
|
use FireflyIII\Handlers\Observer\AccountObserver;
|
||||||
use FireflyIII\Handlers\Observer\AttachmentObserver;
|
use FireflyIII\Handlers\Observer\AttachmentObserver;
|
||||||
use FireflyIII\Handlers\Observer\AutoBudgetObserver;
|
use FireflyIII\Handlers\Observer\AutoBudgetObserver;
|
||||||
@@ -114,150 +115,153 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
protected $listen
|
protected $listen
|
||||||
= [
|
= [
|
||||||
// is a User related event.
|
// is a User related event.
|
||||||
RegisteredUser::class => [
|
RegisteredUser::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendAdminRegistrationNotification',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendAdminRegistrationNotification',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole',
|
'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@createGroupMembership',
|
'FireflyIII\Handlers\Events\UserEventHandler@createGroupMembership',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@createExchangeRates',
|
'FireflyIII\Handlers\Events\UserEventHandler@createExchangeRates',
|
||||||
],
|
],
|
||||||
UserAttemptedLogin::class => [
|
UserAttemptedLogin::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendLoginAttemptNotification',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendLoginAttemptNotification',
|
||||||
],
|
],
|
||||||
// is a User related event.
|
// is a User related event.
|
||||||
Login::class => [
|
Login::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
||||||
],
|
],
|
||||||
ActuallyLoggedIn::class => [
|
ActuallyLoggedIn::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
||||||
],
|
],
|
||||||
DetectedNewIPAddress::class => [
|
DetectedNewIPAddress::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress',
|
'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress',
|
||||||
],
|
],
|
||||||
RequestedVersionCheckStatus::class => [
|
RequestedVersionCheckStatus::class => [
|
||||||
'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates',
|
'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates',
|
||||||
],
|
],
|
||||||
RequestedReportOnJournals::class => [
|
RequestedReportOnJournals::class => [
|
||||||
'FireflyIII\Handlers\Events\AutomationHandler@reportJournals',
|
'FireflyIII\Handlers\Events\AutomationHandler@reportJournals',
|
||||||
],
|
],
|
||||||
|
|
||||||
// is a User related event.
|
// is a User related event.
|
||||||
RequestedNewPassword::class => [
|
RequestedNewPassword::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword',
|
||||||
],
|
],
|
||||||
UserTestNotificationChannel::class => [
|
UserTestNotificationChannel::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendTestNotification',
|
||||||
],
|
],
|
||||||
// is a User related event.
|
// is a User related event.
|
||||||
UserChangedEmail::class => [
|
UserChangedEmail::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail',
|
||||||
],
|
],
|
||||||
// admin related
|
// admin related
|
||||||
OwnerTestNotificationChannel::class => [
|
OwnerTestNotificationChannel::class => [
|
||||||
'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification',
|
'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification',
|
||||||
],
|
],
|
||||||
NewVersionAvailable::class => [
|
NewVersionAvailable::class => [
|
||||||
'FireflyIII\Handlers\Events\AdminEventHandler@sendNewVersion',
|
'FireflyIII\Handlers\Events\AdminEventHandler@sendNewVersion',
|
||||||
],
|
],
|
||||||
InvitationCreated::class => [
|
InvitationCreated::class => [
|
||||||
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
|
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
|
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
|
||||||
],
|
],
|
||||||
UnknownUserAttemptedLogin::class => [
|
UnknownUserAttemptedLogin::class => [
|
||||||
'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification',
|
'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification',
|
||||||
],
|
],
|
||||||
|
|
||||||
// is a Transaction Journal related event.
|
// is a Transaction Journal related event.
|
||||||
StoredTransactionGroup::class => [
|
StoredTransactionGroup::class => [
|
||||||
'FireflyIII\Handlers\Events\StoredGroupEventHandler@runAllHandlers',
|
'FireflyIII\Handlers\Events\StoredGroupEventHandler@runAllHandlers',
|
||||||
],
|
],
|
||||||
// is a Transaction Journal related event.
|
// is a Transaction Journal related event.
|
||||||
UpdatedTransactionGroup::class => [
|
UpdatedTransactionGroup::class => [
|
||||||
'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@runAllHandlers',
|
'FireflyIII\Handlers\Events\UpdatedGroupEventHandler@runAllHandlers',
|
||||||
],
|
],
|
||||||
DestroyedTransactionGroup::class => [
|
DestroyedTransactionGroup::class => [
|
||||||
'FireflyIII\Handlers\Events\DestroyedGroupEventHandler@runAllHandlers',
|
'FireflyIII\Handlers\Events\DestroyedGroupEventHandler@runAllHandlers',
|
||||||
],
|
],
|
||||||
// API related events:
|
// API related events:
|
||||||
AccessTokenCreated::class => [
|
AccessTokenCreated::class => [
|
||||||
'FireflyIII\Handlers\Events\APIEventHandler@accessTokenCreated',
|
'FireflyIII\Handlers\Events\APIEventHandler@accessTokenCreated',
|
||||||
],
|
],
|
||||||
|
|
||||||
// Webhook related event:
|
// Webhook related event:
|
||||||
RequestedSendWebhookMessages::class => [
|
RequestedSendWebhookMessages::class => [
|
||||||
'FireflyIII\Handlers\Events\WebhookEventHandler@sendWebhookMessages',
|
'FireflyIII\Handlers\Events\WebhookEventHandler@sendWebhookMessages',
|
||||||
],
|
],
|
||||||
|
|
||||||
// account related events:
|
// account related events:
|
||||||
StoredAccount::class => [
|
StoredAccount::class => [
|
||||||
'FireflyIII\Handlers\Events\StoredAccountEventHandler@recalculateCredit',
|
'FireflyIII\Handlers\Events\StoredAccountEventHandler@recalculateCredit',
|
||||||
],
|
],
|
||||||
UpdatedAccount::class => [
|
UpdatedAccount::class => [
|
||||||
'FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit',
|
'FireflyIII\Handlers\Events\UpdatedAccountEventHandler@recalculateCredit',
|
||||||
],
|
],
|
||||||
|
|
||||||
// bill related events:
|
// bill related events:
|
||||||
WarnUserAboutBill::class => [
|
WarnUserAboutBill::class => [
|
||||||
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill',
|
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutBill',
|
||||||
],
|
],
|
||||||
|
WarnUserAboutOverdueSubscriptions::class => [
|
||||||
|
'FireflyIII\Handlers\Events\BillEventHandler@warnAboutOverdueSubscriptions',
|
||||||
|
],
|
||||||
|
|
||||||
// audit log events:
|
// audit log events:
|
||||||
TriggeredAuditLog::class => [
|
TriggeredAuditLog::class => [
|
||||||
'FireflyIII\Handlers\Events\AuditEventHandler@storeAuditEvent',
|
'FireflyIII\Handlers\Events\AuditEventHandler@storeAuditEvent',
|
||||||
],
|
],
|
||||||
// piggy bank related events:
|
// piggy bank related events:
|
||||||
ChangedAmount::class => [
|
ChangedAmount::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount',
|
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changePiggyAmount',
|
||||||
],
|
],
|
||||||
ChangedName::class => [
|
ChangedName::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
|
'FireflyIII\Handlers\Events\Model\PiggyBankEventHandler@changedPiggyBankName',
|
||||||
],
|
],
|
||||||
|
|
||||||
// budget related events: CRUD budget limit
|
// budget related events: CRUD budget limit
|
||||||
Created::class => [
|
Created::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
|
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@created',
|
||||||
],
|
],
|
||||||
Updated::class => [
|
Updated::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
|
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@updated',
|
||||||
],
|
],
|
||||||
Deleted::class => [
|
Deleted::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
|
'FireflyIII\Handlers\Events\Model\BudgetLimitHandler@deleted',
|
||||||
],
|
],
|
||||||
|
|
||||||
// rule actions
|
// rule actions
|
||||||
RuleActionFailedOnArray::class => [
|
RuleActionFailedOnArray::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
|
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnArray',
|
||||||
],
|
],
|
||||||
RuleActionFailedOnObject::class => [
|
RuleActionFailedOnObject::class => [
|
||||||
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
|
'FireflyIII\Handlers\Events\Model\RuleHandler@ruleActionFailedOnObject',
|
||||||
],
|
],
|
||||||
|
|
||||||
// security related
|
// security related
|
||||||
EnabledMFA::class => [
|
EnabledMFA::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAEnabledMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAEnabledMail',
|
||||||
],
|
],
|
||||||
DisabledMFA::class => [
|
DisabledMFA::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFADisabledMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFADisabledMail',
|
||||||
],
|
],
|
||||||
MFANewBackupCodes::class => [
|
MFANewBackupCodes::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendNewMFABackupCodesMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendNewMFABackupCodesMail',
|
||||||
],
|
],
|
||||||
MFAUsedBackupCode::class => [
|
MFAUsedBackupCode::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendUsedBackupCodeMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendUsedBackupCodeMail',
|
||||||
],
|
],
|
||||||
MFABackupFewLeft::class => [
|
MFABackupFewLeft::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupFewLeftMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupFewLeftMail',
|
||||||
],
|
],
|
||||||
MFABackupNoLeft::class => [
|
MFABackupNoLeft::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupNoLeftMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendBackupNoLeftMail',
|
||||||
],
|
],
|
||||||
MFAManyFailedAttempts::class => [
|
MFAManyFailedAttempts::class => [
|
||||||
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAFailedAttemptsMail',
|
'FireflyIII\Handlers\Events\Security\MFAHandler@sendMFAFailedAttemptsMail',
|
||||||
],
|
],
|
||||||
// preferences
|
// preferences
|
||||||
UserGroupChangedPrimaryCurrency::class => [
|
UserGroupChangedPrimaryCurrency::class => [
|
||||||
'FireflyIII\Handlers\Events\PreferencesEventHandler@resetPrimaryCurrencyAmounts',
|
'FireflyIII\Handlers\Events\PreferencesEventHandler@resetPrimaryCurrencyAmounts',
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -562,7 +562,13 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
|
|||||||
'foreign_currencies.decimal_places as foreign_currency_decimal_places',
|
'foreign_currencies.decimal_places as foreign_currency_decimal_places',
|
||||||
|
|
||||||
// fields
|
// fields
|
||||||
'transaction_journals.date', 'transaction_types.type', 'transaction_journals.transaction_currency_id', 'transactions.amount'])
|
'transaction_journals.date',
|
||||||
|
'transaction_types.type',
|
||||||
|
'transaction_journals.transaction_currency_id',
|
||||||
|
'transactions.amount',
|
||||||
|
'transactions.native_amount as pc_amount',
|
||||||
|
'transactions.foreign_amount',
|
||||||
|
])
|
||||||
->toArray()
|
->toArray()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|||||||
@@ -50,10 +50,10 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
|
|||||||
$yesterday = clone $start;
|
$yesterday = clone $start;
|
||||||
$yesterday->subDay()->endOfDay(); // exactly up until $start but NOT including.
|
$yesterday->subDay()->endOfDay(); // exactly up until $start but NOT including.
|
||||||
$end->endOfDay(); // needs to be end of day to be correct.
|
$end->endOfDay(); // needs to be end of day to be correct.
|
||||||
Log::debug(sprintf('getAccountReport: finalAccountsBalance("%s")', $yesterday->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $yesterday->format('Y-m-d H:i:s')));
|
||||||
Log::debug(sprintf('getAccountReport: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||||
$startSet = Steam::finalAccountsBalance($accounts, $yesterday);
|
$startSet = Steam::accountsBalancesOptimized($accounts, $yesterday);
|
||||||
$endSet = Steam::finalAccountsBalance($accounts, $end);
|
$endSet = Steam::accountsBalancesOptimized($accounts, $end);
|
||||||
Log::debug('Start of accountreport');
|
Log::debug('Start of accountreport');
|
||||||
|
|
||||||
/** @var AccountRepositoryInterface $repository */
|
/** @var AccountRepositoryInterface $repository */
|
||||||
|
|||||||
@@ -52,4 +52,10 @@ class ALERepository implements ALERepositoryInterface
|
|||||||
|
|
||||||
return $auditLogEntry;
|
return $auditLogEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getForId(string $model, int $modelId): Collection
|
||||||
|
{
|
||||||
|
// all Models have an ID.
|
||||||
|
return AuditLogEntry::where('auditable_id', $modelId)->where('auditable_type', $model)->get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,5 +46,7 @@ interface ALERepositoryInterface
|
|||||||
{
|
{
|
||||||
public function getForObject(Model $model): Collection;
|
public function getForObject(Model $model): Collection;
|
||||||
|
|
||||||
|
public function getForId(string $model, int $modelId): Collection;
|
||||||
|
|
||||||
public function store(array $data): AuditLogEntry;
|
public function store(array $data): AuditLogEntry;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -202,8 +202,11 @@ class OperationsRepository implements OperationsRepositoryInterface, UserGroupIn
|
|||||||
'amount' => $amount,
|
'amount' => $amount,
|
||||||
'destination_account_id' => $journal['destination_account_id'],
|
'destination_account_id' => $journal['destination_account_id'],
|
||||||
'destination_account_name' => $journal['destination_account_name'],
|
'destination_account_name' => $journal['destination_account_name'],
|
||||||
|
'destination_account_type' => $journal['destination_account_type'],
|
||||||
|
'currency_id' => $journalCurrencyId,
|
||||||
'source_account_id' => $journal['source_account_id'],
|
'source_account_id' => $journal['source_account_id'],
|
||||||
'source_account_name' => $journal['source_account_name'],
|
'source_account_name' => $journal['source_account_name'],
|
||||||
|
'source_account_type' => $journal['source_account_type'],
|
||||||
'category_name' => $journal['category_name'],
|
'category_name' => $journal['category_name'],
|
||||||
'description' => $journal['description'],
|
'description' => $journal['description'],
|
||||||
'transaction_group_id' => $journal['transaction_group_id'],
|
'transaction_group_id' => $journal['transaction_group_id'],
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use GuzzleHttp\Client;
|
|||||||
use GuzzleHttp\Exception\ConnectException;
|
use GuzzleHttp\Exception\ConnectException;
|
||||||
use GuzzleHttp\Exception\GuzzleException;
|
use GuzzleHttp\Exception\GuzzleException;
|
||||||
use GuzzleHttp\Exception\RequestException;
|
use GuzzleHttp\Exception\RequestException;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
use JsonException;
|
use JsonException;
|
||||||
|
|
||||||
use function Safe\json_encode;
|
use function Safe\json_encode;
|
||||||
@@ -65,9 +66,9 @@ class StandardWebhookSender implements WebhookSenderInterface
|
|||||||
try {
|
try {
|
||||||
$signature = $signatureGenerator->generate($this->message);
|
$signature = $signatureGenerator->generate($this->message);
|
||||||
} catch (FireflyException $e) {
|
} catch (FireflyException $e) {
|
||||||
app('log')->error('Did not send message because of a Firefly III Exception.');
|
Log::error('Did not send message because of a Firefly III Exception.');
|
||||||
app('log')->error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
app('log')->error($e->getTraceAsString());
|
Log::error($e->getTraceAsString());
|
||||||
$attempt = new WebhookAttempt();
|
$attempt = new WebhookAttempt();
|
||||||
$attempt->webhookMessage()->associate($this->message);
|
$attempt->webhookMessage()->associate($this->message);
|
||||||
$attempt->status_code = 0;
|
$attempt->status_code = 0;
|
||||||
@@ -80,14 +81,14 @@ class StandardWebhookSender implements WebhookSenderInterface
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
app('log')->debug(sprintf('Trying to send webhook message #%d', $this->message->id));
|
Log::debug(sprintf('Trying to send webhook message #%d', $this->message->id));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$json = json_encode($this->message->message, JSON_THROW_ON_ERROR);
|
$json = json_encode($this->message->message, JSON_THROW_ON_ERROR);
|
||||||
} catch (JsonException $e) {
|
} catch (JsonException $e) {
|
||||||
app('log')->error('Did not send message because of a JSON error.');
|
Log::error('Did not send message because of a JSON error.');
|
||||||
app('log')->error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
app('log')->error($e->getTraceAsString());
|
Log::error($e->getTraceAsString());
|
||||||
$attempt = new WebhookAttempt();
|
$attempt = new WebhookAttempt();
|
||||||
$attempt->webhookMessage()->associate($this->message);
|
$attempt->webhookMessage()->associate($this->message);
|
||||||
$attempt->status_code = 0;
|
$attempt->status_code = 0;
|
||||||
@@ -115,9 +116,9 @@ class StandardWebhookSender implements WebhookSenderInterface
|
|||||||
try {
|
try {
|
||||||
$res = $client->request('POST', $this->message->webhook->url, $options);
|
$res = $client->request('POST', $this->message->webhook->url, $options);
|
||||||
} catch (ConnectException|RequestException $e) {
|
} catch (ConnectException|RequestException $e) {
|
||||||
app('log')->error('The webhook could NOT be submitted but Firefly III caught the error below.');
|
Log::error('The webhook could NOT be submitted but Firefly III caught the error below.');
|
||||||
app('log')->error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
app('log')->error($e->getTraceAsString());
|
Log::error($e->getTraceAsString());
|
||||||
|
|
||||||
$logs = sprintf("%s\n%s", $e->getMessage(), $e->getTraceAsString());
|
$logs = sprintf("%s\n%s", $e->getMessage(), $e->getTraceAsString());
|
||||||
|
|
||||||
@@ -130,9 +131,9 @@ class StandardWebhookSender implements WebhookSenderInterface
|
|||||||
$attempt->status_code = 0;
|
$attempt->status_code = 0;
|
||||||
if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse')) {
|
if (method_exists($e, 'hasResponse') && method_exists($e, 'getResponse')) {
|
||||||
$attempt->status_code = $e->hasResponse() ? $e->getResponse()->getStatusCode() : 0;
|
$attempt->status_code = $e->hasResponse() ? $e->getResponse()->getStatusCode() : 0;
|
||||||
app('log')->error(sprintf('The status code of the error response is: %d', $attempt->status_code));
|
Log::error(sprintf('The status code of the error response is: %d', $attempt->status_code));
|
||||||
$body = (string) ($e->hasResponse() ? $e->getResponse()->getBody() : '');
|
$body = (string) ($e->hasResponse() ? $e->getResponse()->getBody() : '');
|
||||||
app('log')->error(sprintf('The body of the error response is: %s', $body));
|
Log::error(sprintf('The body of the error response is: %s', $body));
|
||||||
}
|
}
|
||||||
$attempt->logs = $logs;
|
$attempt->logs = $logs;
|
||||||
$attempt->save();
|
$attempt->save();
|
||||||
@@ -142,9 +143,9 @@ class StandardWebhookSender implements WebhookSenderInterface
|
|||||||
$this->message->sent = true;
|
$this->message->sent = true;
|
||||||
$this->message->save();
|
$this->message->save();
|
||||||
|
|
||||||
app('log')->debug(sprintf('Webhook message #%d was sent. Status code %d', $this->message->id, $res->getStatusCode()));
|
Log::debug(sprintf('Webhook message #%d was sent. Status code %d', $this->message->id, $res->getStatusCode()));
|
||||||
app('log')->debug(sprintf('Webhook request body size: %d bytes', strlen($json)));
|
Log::debug(sprintf('Webhook request body size: %d bytes', strlen($json)));
|
||||||
app('log')->debug(sprintf('Response body: %s', $res->getBody()));
|
Log::debug(sprintf('Response body: %s', $res->getBody()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setMessage(WebhookMessage $message): void
|
public function setMessage(WebhookMessage $message): void
|
||||||
|
|||||||
@@ -29,9 +29,9 @@ use FireflyIII\Models\TransactionCurrency;
|
|||||||
use FireflyIII\Models\TransactionJournal;
|
use FireflyIII\Models\TransactionJournal;
|
||||||
use FireflyIII\Models\UserGroup;
|
use FireflyIII\Models\UserGroup;
|
||||||
use FireflyIII\Support\Facades\Preferences;
|
use FireflyIII\Support\Facades\Preferences;
|
||||||
|
use FireflyIII\Support\Singleton\PreferencesSingleton;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Log;
|
|
||||||
use NumberFormatter;
|
use NumberFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,11 +116,28 @@ class Amount
|
|||||||
|
|
||||||
public function convertToPrimary(?User $user = null): bool
|
public function convertToPrimary(?User $user = null): bool
|
||||||
{
|
{
|
||||||
|
$instance = PreferencesSingleton::getInstance();
|
||||||
if (!$user instanceof User) {
|
if (!$user instanceof User) {
|
||||||
return true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled');
|
$pref = $instance->getPreference('convert_to_primary_no_user');
|
||||||
|
if (null === $pref) {
|
||||||
|
$res = true === Preferences::get('convert_to_primary', false)->data && true === config('cer.enabled');
|
||||||
|
$instance->setPreference('convert_to_primary_no_user', $res);
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pref;
|
||||||
|
}
|
||||||
|
$key = sprintf('convert_to_primary_%d', $user->id);
|
||||||
|
$pref = $instance->getPreference($key);
|
||||||
|
if (null === $pref) {
|
||||||
|
$res = true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
|
||||||
|
$instance->setPreference($key, $res);
|
||||||
|
|
||||||
|
return $res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true === Preferences::getForUser($user, 'convert_to_primary', false)->data && true === config('cer.enabled');
|
return $pref;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPrimaryCurrency(): TransactionCurrency
|
public function getPrimaryCurrency(): TransactionCurrency
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Jobs\CreateAutoBudgetLimits;
|
use FireflyIII\Jobs\CreateAutoBudgetLimits;
|
||||||
use FireflyIII\Models\Configuration;
|
use FireflyIII\Models\Configuration;
|
||||||
use FireflyIII\Support\Facades\FireflyConfig;
|
use FireflyIII\Support\Facades\FireflyConfig;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class AutoBudgetCronjob
|
* Class AutoBudgetCronjob
|
||||||
@@ -42,22 +43,22 @@ class AutoBudgetCronjob extends AbstractCronjob
|
|||||||
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
||||||
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
||||||
if (0 === $lastTime) {
|
if (0 === $lastTime) {
|
||||||
app('log')->info('Auto budget cron-job has never fired before.');
|
Log::info('Auto budget cron-job has never fired before.');
|
||||||
}
|
}
|
||||||
// less than half a day ago:
|
// less than half a day ago:
|
||||||
if ($lastTime > 0 && $diff <= 43200) {
|
if ($lastTime > 0 && $diff <= 43200) {
|
||||||
app('log')->info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans));
|
Log::info(sprintf('It has been %s since the auto budget cron-job has fired.', $diffForHumans));
|
||||||
if (false === $this->force) {
|
if (false === $this->force) {
|
||||||
app('log')->info('The auto budget cron-job will not fire now.');
|
Log::info('The auto budget cron-job will not fire now.');
|
||||||
$this->message = sprintf('It has been %s since the auto budget cron-job has fired. It will not fire now.', $diffForHumans);
|
$this->message = sprintf('It has been %s since the auto budget cron-job has fired. It will not fire now.', $diffForHumans);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
app('log')->info('Execution of the auto budget cron-job has been FORCED.');
|
Log::info('Execution of the auto budget cron-job has been FORCED.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($lastTime > 0 && $diff > 43200) {
|
if ($lastTime > 0 && $diff > 43200) {
|
||||||
app('log')->info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans));
|
Log::info(sprintf('It has been %s since the auto budget cron-job has fired. It will fire now!', $diffForHumans));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->fireAutoBudget();
|
$this->fireAutoBudget();
|
||||||
@@ -66,7 +67,7 @@ class AutoBudgetCronjob extends AbstractCronjob
|
|||||||
|
|
||||||
private function fireAutoBudget(): void
|
private function fireAutoBudget(): void
|
||||||
{
|
{
|
||||||
app('log')->info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d')));
|
Log::info(sprintf('Will now fire auto budget cron job task for date "%s".', $this->date->format('Y-m-d')));
|
||||||
|
|
||||||
/** @var CreateAutoBudgetLimits $job */
|
/** @var CreateAutoBudgetLimits $job */
|
||||||
$job = app(CreateAutoBudgetLimits::class, [$this->date]);
|
$job = app(CreateAutoBudgetLimits::class, [$this->date]);
|
||||||
@@ -80,6 +81,6 @@ class AutoBudgetCronjob extends AbstractCronjob
|
|||||||
$this->message = 'Auto-budget cron job fired successfully.';
|
$this->message = 'Auto-budget cron job fired successfully.';
|
||||||
|
|
||||||
FireflyConfig::set('last_ab_job', (int) $this->date->format('U'));
|
FireflyConfig::set('last_ab_job', (int) $this->date->format('U'));
|
||||||
app('log')->info('Done with auto budget cron job task.');
|
Log::info('Done with auto budget cron job task.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Jobs\WarnAboutBills;
|
use FireflyIII\Jobs\WarnAboutBills;
|
||||||
use FireflyIII\Models\Configuration;
|
use FireflyIII\Models\Configuration;
|
||||||
|
use FireflyIII\Support\Facades\FireflyConfig;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class BillWarningCronjob
|
* Class BillWarningCronjob
|
||||||
@@ -39,22 +41,22 @@ class BillWarningCronjob extends AbstractCronjob
|
|||||||
*/
|
*/
|
||||||
public function fire(): void
|
public function fire(): void
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Now in %s', __METHOD__));
|
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||||
|
|
||||||
/** @var Configuration $config */
|
/** @var Configuration $config */
|
||||||
$config = app('fireflyconfig')->get('last_bw_job', 0);
|
$config = FireflyConfig::get('last_bw_job', 0);
|
||||||
$lastTime = (int) $config->data;
|
$lastTime = (int) $config->data;
|
||||||
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
||||||
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
||||||
|
|
||||||
if (0 === $lastTime) {
|
if (0 === $lastTime) {
|
||||||
app('log')->info('The bill notification cron-job has never fired before.');
|
Log::info('The bill notification cron-job has never fired before.');
|
||||||
}
|
}
|
||||||
// less than half a day ago:
|
// less than half a day ago:
|
||||||
if ($lastTime > 0 && $diff <= 43200) {
|
if ($lastTime > 0 && $diff <= 43200) {
|
||||||
app('log')->info(sprintf('It has been %s since the bill notification cron-job has fired.', $diffForHumans));
|
Log::info(sprintf('It has been %s since the bill notification cron-job has fired.', $diffForHumans));
|
||||||
if (false === $this->force) {
|
if (false === $this->force) {
|
||||||
app('log')->info('The cron-job will not fire now.');
|
Log::info('The cron-job will not fire now.');
|
||||||
$this->message = sprintf('It has been %s since the bill notification cron-job has fired. It will not fire now.', $diffForHumans);
|
$this->message = sprintf('It has been %s since the bill notification cron-job has fired. It will not fire now.', $diffForHumans);
|
||||||
$this->jobFired = false;
|
$this->jobFired = false;
|
||||||
$this->jobErrored = false;
|
$this->jobErrored = false;
|
||||||
@@ -63,11 +65,11 @@ class BillWarningCronjob extends AbstractCronjob
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
app('log')->info('Execution of the bill notification cron-job has been FORCED.');
|
Log::info('Execution of the bill notification cron-job has been FORCED.');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($lastTime > 0 && $diff > 43200) {
|
if ($lastTime > 0 && $diff > 43200) {
|
||||||
app('log')->info(sprintf('It has been %s since the bill notification cron-job has fired. It will fire now!', $diffForHumans));
|
Log::info(sprintf('It has been %s since the bill notification cron-job has fired. It will fire now!', $diffForHumans));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->fireWarnings();
|
$this->fireWarnings();
|
||||||
@@ -77,7 +79,7 @@ class BillWarningCronjob extends AbstractCronjob
|
|||||||
|
|
||||||
private function fireWarnings(): void
|
private function fireWarnings(): void
|
||||||
{
|
{
|
||||||
app('log')->info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
|
Log::info(sprintf('Will now fire bill notification job task for date "%s".', $this->date->format('Y-m-d H:i:s')));
|
||||||
|
|
||||||
/** @var WarnAboutBills $job */
|
/** @var WarnAboutBills $job */
|
||||||
$job = app(WarnAboutBills::class);
|
$job = app(WarnAboutBills::class);
|
||||||
@@ -91,8 +93,8 @@ class BillWarningCronjob extends AbstractCronjob
|
|||||||
$this->jobSucceeded = true;
|
$this->jobSucceeded = true;
|
||||||
$this->message = 'Bill notification cron job fired successfully.';
|
$this->message = 'Bill notification cron job fired successfully.';
|
||||||
|
|
||||||
app('fireflyconfig')->set('last_bw_job', (int) $this->date->format('U'));
|
FireflyConfig::set('last_bw_job', (int) $this->date->format('U'));
|
||||||
app('log')->info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U')));
|
Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U')));
|
||||||
app('log')->info('Done with bill notification cron job task.');
|
Log::info('Done with bill notification cron job task.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace FireflyIII\Support\Cronjobs;
|
|||||||
use Carbon\Carbon;
|
use Carbon\Carbon;
|
||||||
use FireflyIII\Jobs\DownloadExchangeRates;
|
use FireflyIII\Jobs\DownloadExchangeRates;
|
||||||
use FireflyIII\Models\Configuration;
|
use FireflyIII\Models\Configuration;
|
||||||
|
use FireflyIII\Support\Facades\FireflyConfig;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -37,7 +38,7 @@ class ExchangeRatesCronjob extends AbstractCronjob
|
|||||||
public function fire(): void
|
public function fire(): void
|
||||||
{
|
{
|
||||||
/** @var Configuration $config */
|
/** @var Configuration $config */
|
||||||
$config = app('fireflyconfig')->get('last_cer_job', 0);
|
$config = FireflyConfig::get('last_cer_job', 0);
|
||||||
$lastTime = (int) $config->data;
|
$lastTime = (int) $config->data;
|
||||||
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
||||||
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
||||||
@@ -80,7 +81,7 @@ class ExchangeRatesCronjob extends AbstractCronjob
|
|||||||
$this->jobSucceeded = true;
|
$this->jobSucceeded = true;
|
||||||
$this->message = 'Exchange rates cron job fired successfully.';
|
$this->message = 'Exchange rates cron job fired successfully.';
|
||||||
|
|
||||||
app('fireflyconfig')->set('last_cer_job', (int) $this->date->format('U'));
|
FireflyConfig::set('last_cer_job', (int) $this->date->format('U'));
|
||||||
Log::info('Done with exchange rates job task.');
|
Log::info('Done with exchange rates job task.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class UpdateCheckCronjob extends AbstractCronjob
|
|||||||
Log::debug('Now in checkForUpdates()');
|
Log::debug('Now in checkForUpdates()');
|
||||||
|
|
||||||
// should not check for updates:
|
// should not check for updates:
|
||||||
$permission = app('fireflyconfig')->get('permission_update_check', -1);
|
$permission = FireflyConfig::get('permission_update_check', -1);
|
||||||
$value = (int) $permission->data;
|
$value = (int) $permission->data;
|
||||||
if (1 !== $value) {
|
if (1 !== $value) {
|
||||||
Log::debug('Update check is not enabled.');
|
Log::debug('Update check is not enabled.');
|
||||||
|
|||||||
97
app/Support/Cronjobs/WebhookCronjob.php
Normal file
97
app/Support/Cronjobs/WebhookCronjob.php
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RecurringCronjob.php
|
||||||
|
* Copyright (c) 2019 james@firefly-iii.org
|
||||||
|
*
|
||||||
|
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Support\Cronjobs;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use FireflyIII\Events\RequestedSendWebhookMessages;
|
||||||
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
|
use FireflyIII\Models\Configuration;
|
||||||
|
use FireflyIII\Support\Facades\FireflyConfig;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class WebhookCronjob
|
||||||
|
*/
|
||||||
|
class WebhookCronjob extends AbstractCronjob
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @throws FireflyException
|
||||||
|
*/
|
||||||
|
public function fire(): void
|
||||||
|
{
|
||||||
|
Log::debug(sprintf('Now in %s', __METHOD__));
|
||||||
|
|
||||||
|
/** @var Configuration $config */
|
||||||
|
$config = FireflyConfig::get('last_webhook_job', 0);
|
||||||
|
$lastTime = (int) $config->data;
|
||||||
|
$diff = Carbon::now()->getTimestamp() - $lastTime;
|
||||||
|
$diffForHumans = today(config('app.timezone'))->diffForHumans(Carbon::createFromTimestamp($lastTime), null, true);
|
||||||
|
|
||||||
|
if (0 === $lastTime) {
|
||||||
|
Log::info('The webhook cron-job has never fired before.');
|
||||||
|
}
|
||||||
|
// less than ten minutes ago.
|
||||||
|
if ($lastTime > 0 && $diff <= 600) {
|
||||||
|
Log::info(sprintf('It has been %s since the webhook cron-job has fired.', $diffForHumans));
|
||||||
|
if (false === $this->force) {
|
||||||
|
Log::info('The cron-job will not fire now.');
|
||||||
|
$this->message = sprintf('It has been %s since the webhook cron-job has fired. It will not fire now.', $diffForHumans);
|
||||||
|
$this->jobFired = false;
|
||||||
|
$this->jobErrored = false;
|
||||||
|
$this->jobSucceeded = false;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Log::info('Execution of the webhook cron-job has been FORCED.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($lastTime > 0 && $diff > 600) {
|
||||||
|
Log::info(sprintf('It has been %s since the webhook cron-job has fired. It will fire now!', $diffForHumans));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fireWebhookmessages();
|
||||||
|
|
||||||
|
app('preferences')->mark();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function fireWebhookmessages(): void
|
||||||
|
{
|
||||||
|
Log::info(sprintf('Will now send webhook messages for date "%s".', $this->date->format('Y-m-d H:i:s')));
|
||||||
|
|
||||||
|
Log::debug('send event RequestedSendWebhookMessages through cron job.');
|
||||||
|
event(new RequestedSendWebhookMessages());
|
||||||
|
|
||||||
|
// get stuff from job:
|
||||||
|
$this->jobFired = true;
|
||||||
|
$this->jobErrored = false;
|
||||||
|
$this->jobSucceeded = true;
|
||||||
|
$this->message = 'Send webhook messages cron job fired successfully.';
|
||||||
|
|
||||||
|
FireflyConfig::set('last_webhook_job', (int) $this->date->format('U'));
|
||||||
|
Log::info(sprintf('Marked the last time this job has run as "%s" (%d)', $this->date->format('Y-m-d H:i:s'), (int) $this->date->format('U')));
|
||||||
|
Log::info('Done with webhook cron job task.');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,19 +28,34 @@ use Illuminate\Support\Facades\Log;
|
|||||||
|
|
||||||
class Timer
|
class Timer
|
||||||
{
|
{
|
||||||
private static array $times = [];
|
private array $times = [];
|
||||||
|
private static ?Timer $instance = null;
|
||||||
|
|
||||||
public static function start(string $title): void
|
private function __construct()
|
||||||
{
|
{
|
||||||
self::$times[$title] = microtime(true);
|
// Private constructor to prevent direct instantiation.
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function stop(string $title): void
|
public static function getInstance(): self
|
||||||
{
|
{
|
||||||
$start = self::$times[$title] ?? 0;
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function start(string $title): void
|
||||||
|
{
|
||||||
|
$this->times[$title] = microtime(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function stop(string $title): void
|
||||||
|
{
|
||||||
|
$start = $this->times[$title] ?? 0;
|
||||||
$end = microtime(true);
|
$end = microtime(true);
|
||||||
$diff = $end - $start;
|
$diff = $end - $start;
|
||||||
unset(self::$times[$title]);
|
unset($this->times[$title]);
|
||||||
Log::debug(sprintf('Timer "%s" took %f seconds', $title, $diff));
|
Log::debug(sprintf('Timer "%s" took %f seconds', $title, $diff));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ trait ChartGeneration
|
|||||||
/** @var AccountRepositoryInterface $accountRepos */
|
/** @var AccountRepositoryInterface $accountRepos */
|
||||||
$accountRepos = app(AccountRepositoryInterface::class);
|
$accountRepos = app(AccountRepositoryInterface::class);
|
||||||
|
|
||||||
$default = app('amount')->getPrimaryCurrency();
|
$primary = app('amount')->getPrimaryCurrency();
|
||||||
$chartData = [];
|
$chartData = [];
|
||||||
|
|
||||||
Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
Log::debug(sprintf('Start of accountBalanceChart(list, %s, %s)', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||||
@@ -75,10 +75,10 @@ trait ChartGeneration
|
|||||||
/** @var Account $account */
|
/** @var Account $account */
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name));
|
Log::debug(sprintf('Now at account #%d ("%s)', $account->id, $account->name));
|
||||||
$currency = $accountRepos->getAccountCurrency($account) ?? $default;
|
$currency = $accountRepos->getAccountCurrency($account) ?? $primary;
|
||||||
$usePrimary = $convertToPrimary && $default->id !== $currency->id;
|
$usePrimary = $convertToPrimary && $primary->id !== $currency->id;
|
||||||
$field = $convertToPrimary ? 'pc_balance' : 'balance';
|
$field = $convertToPrimary ? 'pc_balance' : 'balance';
|
||||||
$currency = $usePrimary ? $default : $currency;
|
$currency = $usePrimary ? $primary : $currency;
|
||||||
Log::debug(sprintf('Will use field %s', $field));
|
Log::debug(sprintf('Will use field %s', $field));
|
||||||
$currentSet = [
|
$currentSet = [
|
||||||
'label' => $account->name,
|
'label' => $account->name,
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
|
|||||||
use FireflyIII\Support\Cronjobs\BillWarningCronjob;
|
use FireflyIII\Support\Cronjobs\BillWarningCronjob;
|
||||||
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
|
use FireflyIII\Support\Cronjobs\ExchangeRatesCronjob;
|
||||||
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
use FireflyIII\Support\Cronjobs\RecurringCronjob;
|
||||||
|
use FireflyIII\Support\Cronjobs\WebhookCronjob;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait CronRunner
|
* Trait CronRunner
|
||||||
@@ -62,6 +63,32 @@ trait CronRunner
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function webhookCronJob(bool $force, Carbon $date): array
|
||||||
|
{
|
||||||
|
/** @var WebhookCronjob $webhook */
|
||||||
|
$webhook = app(WebhookCronjob::class);
|
||||||
|
$webhook->setForce($force);
|
||||||
|
$webhook->setDate($date);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$webhook->fire();
|
||||||
|
} catch (FireflyException $e) {
|
||||||
|
return [
|
||||||
|
'job_fired' => false,
|
||||||
|
'job_succeeded' => false,
|
||||||
|
'job_errored' => true,
|
||||||
|
'message' => $e->getMessage(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'job_fired' => $webhook->jobFired,
|
||||||
|
'job_succeeded' => $webhook->jobSucceeded,
|
||||||
|
'job_errored' => $webhook->jobErrored,
|
||||||
|
'message' => $webhook->message,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected function exchangeRatesCronJob(bool $force, Carbon $date): array
|
protected function exchangeRatesCronJob(bool $force, Carbon $date): array
|
||||||
{
|
{
|
||||||
/** @var ExchangeRatesCronjob $exchangeRates */
|
/** @var ExchangeRatesCronjob $exchangeRates */
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
|||||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||||
use FireflyIII\Support\CacheProperties;
|
use FireflyIII\Support\CacheProperties;
|
||||||
use FireflyIII\Support\Debug\Timer;
|
use FireflyIII\Support\Debug\Timer;
|
||||||
|
use FireflyIII\Support\Facades\Navigation;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -79,9 +80,10 @@ trait PeriodOverview
|
|||||||
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
|
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
Log::debug('Now in getAccountPeriodOverview()');
|
Log::debug('Now in getAccountPeriodOverview()');
|
||||||
Timer::start('account-period-total');
|
$timer = Timer::getInstance();
|
||||||
|
$timer->start('account-period-total');
|
||||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = Navigation::getViewRange(true);
|
||||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||||
|
|
||||||
// properties for cache
|
// properties for cache
|
||||||
@@ -91,32 +93,30 @@ trait PeriodOverview
|
|||||||
$cache->addProperty('account-show-period-entries');
|
$cache->addProperty('account-show-period-entries');
|
||||||
$cache->addProperty($account->id);
|
$cache->addProperty($account->id);
|
||||||
if ($cache->has()) {
|
if ($cache->has()) {
|
||||||
|
Log::debug('Return CACHED in getAccountPeriodOverview()');
|
||||||
|
|
||||||
return $cache->get();
|
return $cache->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var array $dates */
|
/** @var array $dates */
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
$entries = [];
|
||||||
$spent = [];
|
|
||||||
$earned = [];
|
|
||||||
$transferredAway = [];
|
|
||||||
$transferredIn = [];
|
|
||||||
|
|
||||||
// run a custom query because doing this with the collector is MEGA slow.
|
// run a custom query because doing this with the collector is MEGA slow.
|
||||||
|
$timer->start('account-period-collect');
|
||||||
$transactions = $this->accountRepository->periodCollection($account, $start, $end);
|
$transactions = $this->accountRepository->periodCollection($account, $start, $end);
|
||||||
|
$timer->stop('account-period-collect');
|
||||||
// loop dates
|
// loop dates
|
||||||
Log::debug(sprintf('Count of loops: %d', count($dates)));
|
Log::debug(sprintf('Count of loops: %d', count($dates)));
|
||||||
$loops = 0;
|
$loops = 0;
|
||||||
// stop after 10 loops for memory reasons.
|
// stop after 10 loops for memory reasons.
|
||||||
|
$timer->start('account-period-loop');
|
||||||
foreach ($dates as $currentDate) {
|
foreach ($dates as $currentDate) {
|
||||||
$title = app('navigation')->periodShow($currentDate['start'], $currentDate['period']);
|
$title = Navigation::periodShow($currentDate['start'], $currentDate['period']);
|
||||||
if ($loops < 10) {
|
[$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']);
|
||||||
[$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']);
|
[$transactions, $earned] = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $currentDate['start'], $currentDate['end']);
|
||||||
[$transactions, $earned] = $this->filterTransactionsByType(TransactionTypeEnum::DEPOSIT, $transactions, $currentDate['start'], $currentDate['end']);
|
[$transactions, $transferredAway] = $this->filterTransfers('away', $transactions, $currentDate['start'], $currentDate['end']);
|
||||||
[$transactions, $transferredAway] = $this->filterTransfers('away', $transactions, $currentDate['start'], $currentDate['end']);
|
[$transactions, $transferredIn] = $this->filterTransfers('in', $transactions, $currentDate['start'], $currentDate['end']);
|
||||||
[$transactions, $transferredIn] = $this->filterTransfers('in', $transactions, $currentDate['start'], $currentDate['end']);
|
|
||||||
}
|
|
||||||
$entries[]
|
$entries[]
|
||||||
= [
|
= [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
@@ -129,8 +129,9 @@ trait PeriodOverview
|
|||||||
];
|
];
|
||||||
++$loops;
|
++$loops;
|
||||||
}
|
}
|
||||||
|
$timer->stop('account-period-loop');
|
||||||
$cache->store($entries);
|
$cache->store($entries);
|
||||||
Timer::stop('account-period-total');
|
$timer->stop('account-period-total');
|
||||||
Log::debug('End of getAccountPeriodOverview()');
|
Log::debug('End of getAccountPeriodOverview()');
|
||||||
|
|
||||||
return $entries;
|
return $entries;
|
||||||
@@ -138,7 +139,8 @@ trait PeriodOverview
|
|||||||
|
|
||||||
private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array
|
private function filterTransactionsByType(TransactionTypeEnum $type, array $transactions, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$result = [];
|
$result = [];
|
||||||
|
$filtered = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int $index
|
* @var int $index
|
||||||
@@ -146,38 +148,46 @@ trait PeriodOverview
|
|||||||
*/
|
*/
|
||||||
foreach ($transactions as $index => $item) {
|
foreach ($transactions as $index => $item) {
|
||||||
$date = Carbon::parse($item['date']);
|
$date = Carbon::parse($item['date']);
|
||||||
if ($item['type'] === $type->value && $date >= $start && $date <= $end) {
|
$fits = $item['type'] === $type->value && $date >= $start && $date <= $end;
|
||||||
|
if ($fits) {
|
||||||
$result[] = $item;
|
$result[] = $item;
|
||||||
unset($transactions[$index]);
|
unset($transactions[$index]);
|
||||||
}
|
}
|
||||||
|
if (!$fits) {
|
||||||
|
$filtered[] = $item;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$transactions, $result];
|
return [$filtered, $result];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array
|
private function filterTransfers(string $direction, array $transactions, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$result = [];
|
$result = [];
|
||||||
|
$filtered = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var int $index
|
* @var int $index
|
||||||
* @var array $item
|
* @var array $item
|
||||||
*/
|
*/
|
||||||
foreach ($transactions as $index => $item) {
|
foreach ($transactions as $index => $item) {
|
||||||
$date = Carbon::parse($item['date']);
|
$date = Carbon::parse($item['date']);
|
||||||
if ($date >= $start && $date <= $end) {
|
if ($date >= $start && $date <= $end) {
|
||||||
if ('away' === $direction && -1 === bccomp((string) $item['amount'], '0')) {
|
if ('away' === $direction && -1 === bccomp((string)$item['amount'], '0')) {
|
||||||
$result[] = $item;
|
$result[] = $item;
|
||||||
unset($transactions[$index]);
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if ('in' === $direction && 1 === bccomp((string) $item['amount'], '0')) {
|
if ('in' === $direction && 1 === bccomp((string)$item['amount'], '0')) {
|
||||||
$result[] = $item;
|
$result[] = $item;
|
||||||
unset($transactions[$index]);
|
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$filtered[] = $item;
|
||||||
}
|
}
|
||||||
|
|
||||||
return [$transactions, $result];
|
return [$filtered, $result];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function groupByCurrency(array $journals): array
|
private function groupByCurrency(array $journals): array
|
||||||
@@ -186,7 +196,7 @@ trait PeriodOverview
|
|||||||
|
|
||||||
/** @var array $journal */
|
/** @var array $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
$currencyId = (int) $journal['currency_id'];
|
$currencyId = (int)$journal['currency_id'];
|
||||||
$currencyCode = $journal['currency_code'];
|
$currencyCode = $journal['currency_code'];
|
||||||
$currencyName = $journal['currency_name'];
|
$currencyName = $journal['currency_name'];
|
||||||
$currencySymbol = $journal['currency_symbol'];
|
$currencySymbol = $journal['currency_symbol'];
|
||||||
@@ -203,7 +213,7 @@ trait PeriodOverview
|
|||||||
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
$currencyDecimalPlaces = $this->primaryCurrency->decimal_places;
|
||||||
}
|
}
|
||||||
if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId === $this->primaryCurrency->id) {
|
if ($this->convertToPrimary && $currencyId !== $this->primaryCurrency->id && $foreignCurrencyId === $this->primaryCurrency->id) {
|
||||||
$currencyId = (int) $foreignCurrencyId;
|
$currencyId = (int)$foreignCurrencyId;
|
||||||
$currencyCode = $journal['foreign_currency_code'];
|
$currencyCode = $journal['foreign_currency_code'];
|
||||||
$currencyName = $journal['foreign_currency_name'];
|
$currencyName = $journal['foreign_currency_name'];
|
||||||
$currencySymbol = $journal['foreign_currency_symbol'];
|
$currencySymbol = $journal['foreign_currency_symbol'];
|
||||||
@@ -235,7 +245,7 @@ trait PeriodOverview
|
|||||||
*/
|
*/
|
||||||
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
|
protected function getCategoryPeriodOverview(Category $category, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = Navigation::getViewRange(true);
|
||||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||||
|
|
||||||
// properties for entries with their amounts.
|
// properties for entries with their amounts.
|
||||||
@@ -251,7 +261,7 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @var array $dates */
|
/** @var array $dates */
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
$entries = [];
|
||||||
|
|
||||||
// collect all expenses in this period:
|
// collect all expenses in this period:
|
||||||
@@ -281,7 +291,7 @@ trait PeriodOverview
|
|||||||
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
||||||
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
||||||
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
||||||
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
|
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
||||||
$entries[]
|
$entries[]
|
||||||
= [
|
= [
|
||||||
'transactions' => 0,
|
'transactions' => 0,
|
||||||
@@ -327,7 +337,7 @@ trait PeriodOverview
|
|||||||
*/
|
*/
|
||||||
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
|
protected function getNoBudgetPeriodOverview(Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = Navigation::getViewRange(true);
|
||||||
|
|
||||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||||
|
|
||||||
@@ -342,7 +352,7 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @var array $dates */
|
/** @var array $dates */
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
$entries = [];
|
||||||
|
|
||||||
// get all expenses without a budget.
|
// get all expenses without a budget.
|
||||||
@@ -353,7 +363,7 @@ trait PeriodOverview
|
|||||||
|
|
||||||
foreach ($dates as $currentDate) {
|
foreach ($dates as $currentDate) {
|
||||||
$set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']);
|
$set = $this->filterJournalsByDate($journals, $currentDate['start'], $currentDate['end']);
|
||||||
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
|
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
||||||
$entries[]
|
$entries[]
|
||||||
= [
|
= [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
@@ -380,17 +390,17 @@ trait PeriodOverview
|
|||||||
protected function getNoCategoryPeriodOverview(Carbon $theDate): array
|
protected function getNoCategoryPeriodOverview(Carbon $theDate): array
|
||||||
{
|
{
|
||||||
app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
|
app('log')->debug(sprintf('Now in getNoCategoryPeriodOverview(%s)', $theDate->format('Y-m-d')));
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = Navigation::getViewRange(true);
|
||||||
$first = $this->journalRepos->firstNull();
|
$first = $this->journalRepos->firstNull();
|
||||||
$start = null === $first ? new Carbon() : $first->date;
|
$start = null === $first ? new Carbon() : $first->date;
|
||||||
$end = clone $theDate;
|
$end = clone $theDate;
|
||||||
$end = app('navigation')->endOfPeriod($end, $range);
|
$end = Navigation::endOfPeriod($end, $range);
|
||||||
|
|
||||||
app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
|
app('log')->debug(sprintf('Start for getNoCategoryPeriodOverview() is %s', $start->format('Y-m-d')));
|
||||||
app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
|
app('log')->debug(sprintf('End for getNoCategoryPeriodOverview() is %s', $end->format('Y-m-d')));
|
||||||
|
|
||||||
// properties for cache
|
// properties for cache
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
$entries = [];
|
||||||
|
|
||||||
// collect all expenses in this period:
|
// collect all expenses in this period:
|
||||||
@@ -422,7 +432,7 @@ trait PeriodOverview
|
|||||||
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
||||||
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
||||||
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
||||||
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
|
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
||||||
$entries[]
|
$entries[]
|
||||||
= [
|
= [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
@@ -445,7 +455,7 @@ trait PeriodOverview
|
|||||||
*/
|
*/
|
||||||
protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags.
|
protected function getTagPeriodOverview(Tag $tag, Carbon $start, Carbon $end): array // period overview for tags.
|
||||||
{
|
{
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = Navigation::getViewRange(true);
|
||||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||||
|
|
||||||
// properties for cache
|
// properties for cache
|
||||||
@@ -459,7 +469,7 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @var array $dates */
|
/** @var array $dates */
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
$entries = [];
|
||||||
|
|
||||||
// collect all expenses in this period:
|
// collect all expenses in this period:
|
||||||
@@ -495,7 +505,7 @@ trait PeriodOverview
|
|||||||
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
$spent = $this->filterJournalsByDate($spentSet, $currentDate['start'], $currentDate['end']);
|
||||||
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
$earned = $this->filterJournalsByDate($earnedSet, $currentDate['start'], $currentDate['end']);
|
||||||
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
$transferred = $this->filterJournalsByDate($transferSet, $currentDate['start'], $currentDate['end']);
|
||||||
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
|
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
||||||
$entries[]
|
$entries[]
|
||||||
= [
|
= [
|
||||||
'transactions' => 0,
|
'transactions' => 0,
|
||||||
@@ -540,7 +550,7 @@ trait PeriodOverview
|
|||||||
*/
|
*/
|
||||||
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
|
protected function getTransactionPeriodOverview(string $transactionType, Carbon $start, Carbon $end): array
|
||||||
{
|
{
|
||||||
$range = app('navigation')->getViewRange(true);
|
$range = Navigation::getViewRange(true);
|
||||||
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
|
$types = config(sprintf('firefly.transactionTypesByType.%s', $transactionType));
|
||||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||||
|
|
||||||
@@ -555,7 +565,7 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @var array $dates */
|
/** @var array $dates */
|
||||||
$dates = app('navigation')->blockPeriods($start, $end, $range);
|
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||||
$entries = [];
|
$entries = [];
|
||||||
$spent = [];
|
$spent = [];
|
||||||
$earned = [];
|
$earned = [];
|
||||||
@@ -567,7 +577,7 @@ trait PeriodOverview
|
|||||||
$loops = 0;
|
$loops = 0;
|
||||||
|
|
||||||
foreach ($dates as $currentDate) {
|
foreach ($dates as $currentDate) {
|
||||||
$title = app('navigation')->periodShow($currentDate['end'], $currentDate['period']);
|
$title = Navigation::periodShow($currentDate['end'], $currentDate['period']);
|
||||||
|
|
||||||
if ($loops < 10) {
|
if ($loops < 10) {
|
||||||
// set to correct array
|
// set to correct array
|
||||||
@@ -582,14 +592,14 @@ trait PeriodOverview
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$entries[]
|
$entries[]
|
||||||
= [
|
= [
|
||||||
'title' => $title,
|
'title' => $title,
|
||||||
'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
|
'route' => route('transactions.index', [$transactionType, $currentDate['start']->format('Y-m-d'), $currentDate['end']->format('Y-m-d')]),
|
||||||
'total_transactions' => count($spent) + count($earned) + count($transferred),
|
'total_transactions' => count($spent) + count($earned) + count($transferred),
|
||||||
'spent' => $this->groupByCurrency($spent),
|
'spent' => $this->groupByCurrency($spent),
|
||||||
'earned' => $this->groupByCurrency($earned),
|
'earned' => $this->groupByCurrency($earned),
|
||||||
'transferred' => $this->groupByCurrency($transferred),
|
'transferred' => $this->groupByCurrency($transferred),
|
||||||
];
|
];
|
||||||
++$loops;
|
++$loops;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +615,7 @@ trait PeriodOverview
|
|||||||
|
|
||||||
/** @var array $journal */
|
/** @var array $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
if ($account->id === (int) $journal['source_account_id']) {
|
if ($account->id === (int)$journal['source_account_id']) {
|
||||||
$return[] = $journal;
|
$return[] = $journal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -622,7 +632,7 @@ trait PeriodOverview
|
|||||||
|
|
||||||
/** @var array $journal */
|
/** @var array $journal */
|
||||||
foreach ($journals as $journal) {
|
foreach ($journals as $journal) {
|
||||||
if ($account->id === (int) $journal['destination_account_id']) {
|
if ($account->id === (int)$journal['destination_account_id']) {
|
||||||
$return[] = $journal;
|
$return[] = $journal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ use FireflyIII\Models\AccountMeta;
|
|||||||
use FireflyIII\Models\AccountType;
|
use FireflyIII\Models\AccountType;
|
||||||
use FireflyIII\Models\Location;
|
use FireflyIII\Models\Location;
|
||||||
use FireflyIII\Models\Note;
|
use FireflyIII\Models\Note;
|
||||||
|
use FireflyIII\Models\ObjectGroup;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\UserGroup;
|
use FireflyIII\Models\UserGroup;
|
||||||
use FireflyIII\Support\Facades\Amount;
|
use FireflyIII\Support\Facades\Amount;
|
||||||
@@ -41,6 +42,7 @@ use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
|||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Override;
|
use Override;
|
||||||
|
|
||||||
@@ -51,37 +53,30 @@ use Override;
|
|||||||
*/
|
*/
|
||||||
class AccountEnrichment implements EnrichmentInterface
|
class AccountEnrichment implements EnrichmentInterface
|
||||||
{
|
{
|
||||||
private array $ids;
|
private array $ids = [];
|
||||||
private array $accountTypeIds;
|
private array $accountTypeIds = [];
|
||||||
private array $accountTypes;
|
private array $accountTypes = [];
|
||||||
private Collection $collection;
|
private Collection $collection;
|
||||||
private array $currencies;
|
private array $currencies = [];
|
||||||
private array $locations;
|
private array $locations = [];
|
||||||
private array $meta;
|
private array $meta = [];
|
||||||
private TransactionCurrency $primaryCurrency;
|
private TransactionCurrency $primaryCurrency;
|
||||||
private array $notes;
|
private array $notes = [];
|
||||||
private array $openingBalances;
|
private array $openingBalances = [];
|
||||||
private User $user;
|
private User $user;
|
||||||
private UserGroup $userGroup;
|
private UserGroup $userGroup;
|
||||||
private array $lastActivities;
|
private array $lastActivities = [];
|
||||||
private ?Carbon $date = null;
|
private ?Carbon $date = null;
|
||||||
private bool $convertToPrimary = false;
|
private bool $convertToPrimary;
|
||||||
private array $balances = [];
|
private array $balances = [];
|
||||||
|
private array $objectGroups = [];
|
||||||
|
private array $mappedObjects = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO The account enricher must do conversion from and to the primary currency.
|
* TODO The account enricher must do conversion from and to the primary currency.
|
||||||
*/
|
*/
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->ids = [];
|
|
||||||
$this->openingBalances = [];
|
|
||||||
$this->currencies = [];
|
|
||||||
$this->accountTypeIds = [];
|
|
||||||
$this->accountTypes = [];
|
|
||||||
$this->meta = [];
|
|
||||||
$this->notes = [];
|
|
||||||
$this->lastActivities = [];
|
|
||||||
$this->locations = [];
|
|
||||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||||
$this->convertToPrimary = Amount::convertToPrimary();
|
$this->convertToPrimary = Amount::convertToPrimary();
|
||||||
}
|
}
|
||||||
@@ -113,6 +108,7 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
$this->collectLastActivities();
|
$this->collectLastActivities();
|
||||||
$this->collectLocations();
|
$this->collectLocations();
|
||||||
$this->collectOpeningBalances();
|
$this->collectOpeningBalances();
|
||||||
|
$this->collectObjectGroups();
|
||||||
$this->collectBalances();
|
$this->collectBalances();
|
||||||
$this->appendCollectedData();
|
$this->appendCollectedData();
|
||||||
|
|
||||||
@@ -247,6 +243,9 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
'longitude' => null,
|
'longitude' => null,
|
||||||
'zoom_level' => null,
|
'zoom_level' => null,
|
||||||
],
|
],
|
||||||
|
'object_group_id' => null,
|
||||||
|
'object_group_order' => null,
|
||||||
|
'object_group_title' => null,
|
||||||
'opening_balance_date' => null,
|
'opening_balance_date' => null,
|
||||||
'opening_balance_amount' => null,
|
'opening_balance_amount' => null,
|
||||||
'account_number' => null,
|
'account_number' => null,
|
||||||
@@ -254,6 +253,14 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
'last_activity' => $this->lastActivities[$id] ?? null,
|
'last_activity' => $this->lastActivities[$id] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// add object group if available
|
||||||
|
if (array_key_exists($id, $this->mappedObjects)) {
|
||||||
|
$key = $this->mappedObjects[$id];
|
||||||
|
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
|
||||||
|
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
||||||
|
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
||||||
|
}
|
||||||
|
|
||||||
// if location, add location:
|
// if location, add location:
|
||||||
if (array_key_exists($id, $this->locations)) {
|
if (array_key_exists($id, $this->locations)) {
|
||||||
$meta['location'] = $this->locations[$id];
|
$meta['location'] = $this->locations[$id];
|
||||||
@@ -347,6 +354,28 @@ class AccountEnrichment implements EnrichmentInterface
|
|||||||
$this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary);
|
$this->balances = Steam::accountsBalancesOptimized($this->collection, $this->getDate(), $this->primaryCurrency, $this->convertToPrimary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function collectObjectGroups(): void
|
||||||
|
{
|
||||||
|
$set = DB::table('object_groupables')
|
||||||
|
->whereIn('object_groupable_id', $this->ids)
|
||||||
|
->where('object_groupable_type', Account::class)
|
||||||
|
->get(['object_groupable_id', 'object_group_id'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$ids = array_unique($set->pluck('object_group_id')->toArray());
|
||||||
|
|
||||||
|
foreach ($set as $entry) {
|
||||||
|
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = ObjectGroup::whereIn('id', $ids)->get(['id', 'title', 'order'])->toArray();
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$group['id'] = (int)$group['id'];
|
||||||
|
$group['order'] = (int)$group['order'];
|
||||||
|
$this->objectGroups[(int)$group['id']] = $group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function setDate(?Carbon $date): void
|
public function setDate(?Carbon $date): void
|
||||||
{
|
{
|
||||||
$this->date = $date;
|
$this->date = $date;
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
|
|||||||
private User $user;
|
private User $user;
|
||||||
private UserGroup $userGroup;
|
private UserGroup $userGroup;
|
||||||
private TransactionCurrency $primaryCurrency;
|
private TransactionCurrency $primaryCurrency;
|
||||||
private bool $convertToPrimary = false;
|
private bool $convertToPrimary;
|
||||||
private array $ids = [];
|
private array $ids = [];
|
||||||
private array $currencyIds = [];
|
private array $currencyIds = [];
|
||||||
private array $currencies = [];
|
private array $currencies = [];
|
||||||
@@ -157,7 +157,7 @@ class AvailableBudgetEnrichment implements EnrichmentInterface
|
|||||||
$currency = $this->currencies[$currencyId];
|
$currency = $this->currencies[$currencyId];
|
||||||
$meta = [
|
$meta = [
|
||||||
'currency' => $currency,
|
'currency' => $currency,
|
||||||
'spent_in_budgets' => $this->spentInsideBudgets[$id] ?? [],
|
'spent_in_budgets' => $this->spentInBudgets[$id] ?? [],
|
||||||
'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [],
|
'pc_spent_in_budgets' => $this->pcSpentInBudgets[$id] ?? [],
|
||||||
'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [],
|
'spent_outside_budgets' => $this->spentOutsideBudgets[$id] ?? [],
|
||||||
'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [],
|
'pc_spent_outside_budgets' => $this->pcSpentOutsideBudgets[$id] ?? [],
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Carbon\Carbon;
|
|||||||
use FireflyIII\Models\AutoBudget;
|
use FireflyIII\Models\AutoBudget;
|
||||||
use FireflyIII\Models\Budget;
|
use FireflyIII\Models\Budget;
|
||||||
use FireflyIII\Models\Note;
|
use FireflyIII\Models\Note;
|
||||||
|
use FireflyIII\Models\ObjectGroup;
|
||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Models\UserGroup;
|
use FireflyIII\Models\UserGroup;
|
||||||
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
use FireflyIII\Repositories\Budget\OperationsRepositoryInterface;
|
||||||
@@ -15,23 +16,26 @@ use FireflyIII\Support\Facades\Amount;
|
|||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\Eloquent\Model;
|
use Illuminate\Database\Eloquent\Model;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class BudgetEnrichment implements EnrichmentInterface
|
class BudgetEnrichment implements EnrichmentInterface
|
||||||
{
|
{
|
||||||
private Collection $collection;
|
private Collection $collection;
|
||||||
private bool $convertToPrimary = true;
|
private bool $convertToPrimary;
|
||||||
private TransactionCurrency $primaryCurrency;
|
private TransactionCurrency $primaryCurrency;
|
||||||
private User $user;
|
private User $user;
|
||||||
private UserGroup $userGroup;
|
private UserGroup $userGroup;
|
||||||
private array $ids = [];
|
private array $ids = [];
|
||||||
private array $notes = [];
|
private array $notes = [];
|
||||||
private array $autoBudgets = [];
|
private array $autoBudgets = [];
|
||||||
private array $currencies = [];
|
private array $currencies = [];
|
||||||
private ?Carbon $start = null;
|
private ?Carbon $start = null;
|
||||||
private ?Carbon $end = null;
|
private ?Carbon $end = null;
|
||||||
private array $spent = [];
|
private array $spent = [];
|
||||||
private array $pcSpent = [];
|
private array $pcSpent = [];
|
||||||
|
private array $objectGroups = [];
|
||||||
|
private array $mappedObjects = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@@ -46,6 +50,8 @@ class BudgetEnrichment implements EnrichmentInterface
|
|||||||
$this->collectNotes();
|
$this->collectNotes();
|
||||||
$this->collectAutoBudgets();
|
$this->collectAutoBudgets();
|
||||||
$this->collectExpenses();
|
$this->collectExpenses();
|
||||||
|
$this->collectObjectGroups();
|
||||||
|
|
||||||
$this->appendCollectedData();
|
$this->appendCollectedData();
|
||||||
|
|
||||||
return $this->collection;
|
return $this->collection;
|
||||||
@@ -98,12 +104,25 @@ class BudgetEnrichment implements EnrichmentInterface
|
|||||||
$this->collection = $this->collection->map(function (Budget $item) {
|
$this->collection = $this->collection->map(function (Budget $item) {
|
||||||
$id = (int)$item->id;
|
$id = (int)$item->id;
|
||||||
$meta = [
|
$meta = [
|
||||||
'notes' => $this->notes[$id] ?? null,
|
'object_group_id' => null,
|
||||||
'currency' => $this->currencies[$id] ?? null,
|
'object_group_order' => null,
|
||||||
'auto_budget' => $this->autoBudgets[$id] ?? null,
|
'object_group_title' => null,
|
||||||
'spent' => $this->spent[$id] ?? null,
|
'notes' => $this->notes[$id] ?? null,
|
||||||
'pc_spent' => $this->pcSpent[$id] ?? null,
|
'currency' => $this->currencies[$id] ?? null,
|
||||||
|
'auto_budget' => $this->autoBudgets[$id] ?? null,
|
||||||
|
'spent' => $this->spent[$id] ?? null,
|
||||||
|
'pc_spent' => $this->pcSpent[$id] ?? null,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// add object group if available
|
||||||
|
if (array_key_exists($id, $this->mappedObjects)) {
|
||||||
|
$key = $this->mappedObjects[$id];
|
||||||
|
$meta['object_group_id'] = $this->objectGroups[$key]['id'];
|
||||||
|
$meta['object_group_title'] = $this->objectGroups[$key]['title'];
|
||||||
|
$meta['object_group_order'] = $this->objectGroups[$key]['order'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
$item->meta = $meta;
|
$item->meta = $meta;
|
||||||
|
|
||||||
return $item;
|
return $item;
|
||||||
@@ -154,4 +173,26 @@ class BudgetEnrichment implements EnrichmentInterface
|
|||||||
{
|
{
|
||||||
$this->start = $start;
|
$this->start = $start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function collectObjectGroups(): void
|
||||||
|
{
|
||||||
|
$set = DB::table('object_groupables')
|
||||||
|
->whereIn('object_groupable_id', $this->ids)
|
||||||
|
->where('object_groupable_type', Budget::class)
|
||||||
|
->get(['object_groupable_id', 'object_group_id'])
|
||||||
|
;
|
||||||
|
|
||||||
|
$ids = array_unique($set->pluck('object_group_id')->toArray());
|
||||||
|
|
||||||
|
foreach ($set as $entry) {
|
||||||
|
$this->mappedObjects[(int)$entry->object_groupable_id] = (int)$entry->object_group_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = ObjectGroup::whereIn('id', $ids)->get(['id', 'title', 'order'])->toArray();
|
||||||
|
foreach ($groups as $group) {
|
||||||
|
$group['id'] = (int)$group['id'];
|
||||||
|
$group['order'] = (int)$group['order'];
|
||||||
|
$this->objectGroups[(int)$group['id']] = $group;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
private Collection $budgets;
|
private Collection $budgets;
|
||||||
private array $expenses = [];
|
private array $expenses = [];
|
||||||
private array $pcExpenses = [];
|
private array $pcExpenses = [];
|
||||||
|
private array $currencyIds = [];
|
||||||
|
private array $currencies = [];
|
||||||
private bool $convertToPrimary = true;
|
private bool $convertToPrimary = true;
|
||||||
private TransactionCurrency $primaryCurrency;
|
private TransactionCurrency $primaryCurrency;
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
{
|
{
|
||||||
$this->collection = $collection;
|
$this->collection = $collection;
|
||||||
$this->collectIds();
|
$this->collectIds();
|
||||||
|
$this->collectCurrencies();
|
||||||
$this->collectNotes();
|
$this->collectNotes();
|
||||||
$this->collectBudgets();
|
$this->collectBudgets();
|
||||||
$this->appendCollectedData();
|
$this->appendCollectedData();
|
||||||
@@ -71,14 +74,19 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
|
|
||||||
private function collectIds(): void
|
private function collectIds(): void
|
||||||
{
|
{
|
||||||
$this->start = $this->collection->min('start_date');
|
$this->start = $this->collection->min('start_date');
|
||||||
$this->end = $this->collection->max('end_date');
|
$this->end = $this->collection->max('end_date');
|
||||||
|
|
||||||
/** @var BudgetLimit $limit */
|
/** @var BudgetLimit $limit */
|
||||||
foreach ($this->collection as $limit) {
|
foreach ($this->collection as $limit) {
|
||||||
$this->ids[] = (int)$limit->id;
|
$id = (int)$limit->id;
|
||||||
|
$this->ids[] = $id;
|
||||||
|
if (0 !== (int)$limit->transaction_currency_id) {
|
||||||
|
$this->currencyIds[$id] = (int)$limit->transaction_currency_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->ids = array_unique($this->ids);
|
$this->ids = array_unique($this->ids);
|
||||||
|
$this->currencyIds = array_unique($this->currencyIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function collectNotes(): void
|
private function collectNotes(): void
|
||||||
@@ -98,10 +106,15 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
{
|
{
|
||||||
$this->collection = $this->collection->map(function (BudgetLimit $item) {
|
$this->collection = $this->collection->map(function (BudgetLimit $item) {
|
||||||
$id = (int)$item->id;
|
$id = (int)$item->id;
|
||||||
|
$currencyId = (int)$item->transaction_currency_id;
|
||||||
|
if (0 === $currencyId) {
|
||||||
|
$currencyId = $this->primaryCurrency->id;
|
||||||
|
}
|
||||||
$meta = [
|
$meta = [
|
||||||
'notes' => $this->notes[$id] ?? null,
|
'notes' => $this->notes[$id] ?? null,
|
||||||
'spent' => $this->expenses[$id] ?? [],
|
'spent' => $this->expenses[$id] ?? [],
|
||||||
'pc_spent' => $this->pcExpenses[$id] ?? [],
|
'pc_spent' => $this->pcExpenses[$id] ?? [],
|
||||||
|
'currency' => $this->currencies[$currencyId],
|
||||||
];
|
];
|
||||||
$item->meta = $meta;
|
$item->meta = $meta;
|
||||||
|
|
||||||
@@ -133,4 +146,13 @@ class BudgetLimitEnrichment implements EnrichmentInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function collectCurrencies(): void
|
||||||
|
{
|
||||||
|
$this->currencies[$this->primaryCurrency->id] = $this->primaryCurrency;
|
||||||
|
$currencies = TransactionCurrency::whereIn('id', $this->currencyIds)->whereNot('id', $this->primaryCurrency->id)->get();
|
||||||
|
foreach ($currencies as $currency) {
|
||||||
|
$this->currencies[(int)$currency->id] = $currency;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,15 +25,15 @@ class PiggyBankEnrichment implements EnrichmentInterface
|
|||||||
private User $user;
|
private User $user;
|
||||||
private UserGroup $userGroup;
|
private UserGroup $userGroup;
|
||||||
private Collection $collection;
|
private Collection $collection;
|
||||||
private array $ids;
|
private array $ids = [];
|
||||||
private array $currencyIds = [];
|
private array $currencyIds = [];
|
||||||
private array $currencies = [];
|
private array $currencies = [];
|
||||||
private array $accountIds = [];
|
private array $accountIds = [];
|
||||||
private array $accountCurrencies = [];
|
// private array $accountCurrencies = [];
|
||||||
private array $notes = [];
|
private array $notes = [];
|
||||||
private array $mappedObjects = [];
|
private array $mappedObjects = [];
|
||||||
private TransactionCurrency $primaryCurrency;
|
private TransactionCurrency $primaryCurrency;
|
||||||
private array $amounts = [];
|
private array $amounts = [];
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
@@ -116,12 +116,12 @@ class PiggyBankEnrichment implements EnrichmentInterface
|
|||||||
|
|
||||||
/** @var AccountMeta $item */
|
/** @var AccountMeta $item */
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$accountId = (int)$item->account_id;
|
$accountId = (int)$item->account_id;
|
||||||
$currencyId = (int)$item->data;
|
$currencyId = (int)$item->data;
|
||||||
if (!array_key_exists($currencyId, $this->currencies)) {
|
if (!array_key_exists($currencyId, $this->currencies)) {
|
||||||
$this->currencies[$currencyId] = TransactionCurrency::find($currencyId);
|
$this->currencies[$currencyId] = TransactionCurrency::find($currencyId);
|
||||||
}
|
}
|
||||||
$this->accountCurrencies[$accountId] = $this->currencies[$currencyId];
|
// $this->accountCurrencies[$accountId] = $this->currencies[$currencyId];
|
||||||
}
|
}
|
||||||
|
|
||||||
// get account info.
|
// get account info.
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
|
|||||||
private array $journalIds = [];
|
private array $journalIds = [];
|
||||||
private array $groupIds = [];
|
private array $groupIds = [];
|
||||||
private array $accountIds = [];
|
private array $accountIds = [];
|
||||||
private array $piggybankIds = [];
|
private array $piggyBankIds = [];
|
||||||
private array $accountCurrencies = [];
|
private array $accountCurrencies = [];
|
||||||
private array $currencies = [];
|
private array $currencies = [];
|
||||||
// private bool $convertToPrimary = false;
|
// private bool $convertToPrimary = false;
|
||||||
@@ -72,7 +72,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
|
|||||||
foreach ($this->collection as $event) {
|
foreach ($this->collection as $event) {
|
||||||
$this->ids[] = (int)$event->id;
|
$this->ids[] = (int)$event->id;
|
||||||
$this->journalIds[(int)$event->id] = (int)$event->transaction_journal_id;
|
$this->journalIds[(int)$event->id] = (int)$event->transaction_journal_id;
|
||||||
$this->piggybankIds[(int)$event->id] = (int)$event->piggy_bank_id;
|
$this->piggyBankIds[(int)$event->id] = (int)$event->piggy_bank_id;
|
||||||
}
|
}
|
||||||
$this->ids = array_unique($this->ids);
|
$this->ids = array_unique($this->ids);
|
||||||
// collect groups with journal info.
|
// collect groups with journal info.
|
||||||
@@ -84,7 +84,7 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// collect account info.
|
// collect account info.
|
||||||
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggybankIds)->get(['piggy_bank_id', 'account_id']);
|
$set = DB::table('account_piggy_bank')->whereIn('piggy_bank_id', $this->piggyBankIds)->get(['piggy_bank_id', 'account_id']);
|
||||||
foreach ($set as $item) {
|
foreach ($set as $item) {
|
||||||
$id = (int)$item->piggy_bank_id;
|
$id = (int)$item->piggy_bank_id;
|
||||||
if (!array_key_exists($id, $this->accountIds)) {
|
if (!array_key_exists($id, $this->accountIds)) {
|
||||||
@@ -93,7 +93,6 @@ class PiggyBankEventEnrichment implements EnrichmentInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// get account currency preference for ALL.
|
// get account currency preference for ALL.
|
||||||
// TODO This method does a find in a loop.
|
|
||||||
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
|
$set = AccountMeta::whereIn('account_id', array_values($this->accountIds))->where('name', 'currency_id')->get();
|
||||||
|
|
||||||
/** @var AccountMeta $item */
|
/** @var AccountMeta $item */
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
|
|
||||||
$this->transactions[$id][$transactionId] = [
|
$this->transactions[$id][$transactionId] = [
|
||||||
'id' => (string)$transactionId,
|
'id' => (string)$transactionId,
|
||||||
'recurrence_id' => $id,
|
// 'recurrence_id' => $id,
|
||||||
'transaction_currency_id' => (int)$transaction->transaction_currency_id,
|
'transaction_currency_id' => (int)$transaction->transaction_currency_id,
|
||||||
'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id,
|
'foreign_currency_id' => null === $transaction->foreign_currency_id ? null : (int)$transaction->foreign_currency_id,
|
||||||
'source_id' => (int)$transaction->source_id,
|
'source_id' => (int)$transaction->source_id,
|
||||||
@@ -309,7 +309,7 @@ class RecurringEnrichment implements EnrichmentInterface
|
|||||||
if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) {
|
if (true === $this->convertToPrimary && $currencyId !== (int)$this->primaryCurrency->id) {
|
||||||
$pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']);
|
$pcAmount = $converter->convert($this->currencies[$currencyId], $this->primaryCurrency, today(), $transaction['amount']);
|
||||||
}
|
}
|
||||||
if (null !== $transaction['foreign_amount']) {
|
if (null !== $transaction['foreign_amount'] && null !== $transaction['foreign_currency_id']) {
|
||||||
$foreignCurrencyId = $transaction['foreign_currency_id'];
|
$foreignCurrencyId = $transaction['foreign_currency_id'];
|
||||||
if ($foreignCurrencyId !== $this->primaryCurrency->id) {
|
if ($foreignCurrencyId !== $this->primaryCurrency->id) {
|
||||||
$pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']);
|
$pcForeignAmount = $converter->convert($this->currencies[$foreignCurrencyId], $this->primaryCurrency, today(), $transaction['foreign_amount']);
|
||||||
|
|||||||
@@ -27,15 +27,15 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
|||||||
private User $user;
|
private User $user;
|
||||||
private UserGroup $userGroup;
|
private UserGroup $userGroup;
|
||||||
private Collection $collection;
|
private Collection $collection;
|
||||||
private bool $convertToPrimary = false;
|
private bool $convertToPrimary;
|
||||||
private ?Carbon $start = null;
|
private ?Carbon $start = null;
|
||||||
private ?Carbon $end = null;
|
private ?Carbon $end = null;
|
||||||
private array $subscriptionIds = [];
|
private array $subscriptionIds = [];
|
||||||
private array $objectGroups = [];
|
private array $objectGroups = [];
|
||||||
private array $mappedObjects = [];
|
private array $mappedObjects = [];
|
||||||
private array $paidDates = [];
|
private array $paidDates = [];
|
||||||
private array $notes = [];
|
private array $notes = [];
|
||||||
private array $payDates = [];
|
private array $payDates = [];
|
||||||
private TransactionCurrency $primaryCurrency;
|
private TransactionCurrency $primaryCurrency;
|
||||||
private BillDateCalculator $calculator;
|
private BillDateCalculator $calculator;
|
||||||
|
|
||||||
@@ -56,6 +56,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
|||||||
$this->collectPaidDates();
|
$this->collectPaidDates();
|
||||||
$this->collectPayDates();
|
$this->collectPayDates();
|
||||||
|
|
||||||
|
|
||||||
// TODO clean me up.
|
// TODO clean me up.
|
||||||
|
|
||||||
$notes = $this->notes;
|
$notes = $this->notes;
|
||||||
@@ -344,7 +345,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
|||||||
|
|
||||||
private function getLastPaidDate(array $paidData): ?Carbon
|
private function getLastPaidDate(array $paidData): ?Carbon
|
||||||
{
|
{
|
||||||
Log::debug('getLastPaidDate()');
|
// Log::debug('getLastPaidDate()');
|
||||||
$return = null;
|
$return = null;
|
||||||
foreach ($paidData as $entry) {
|
foreach ($paidData as $entry) {
|
||||||
if (null !== $return) {
|
if (null !== $return) {
|
||||||
@@ -353,15 +354,15 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
|||||||
if ($current->gt($return)) {
|
if ($current->gt($return)) {
|
||||||
$return = clone $current;
|
$return = clone $current;
|
||||||
}
|
}
|
||||||
Log::debug(sprintf('Last paid date is: %s', $return->format('Y-m-d')));
|
Log::debug(sprintf('[a] Last paid date is: %s', $return->format('Y-m-d')));
|
||||||
}
|
}
|
||||||
if (null === $return) {
|
if (null === $return) {
|
||||||
/** @var Carbon $return */
|
/** @var Carbon $return */
|
||||||
$return = $entry['date_object'];
|
$return = $entry['date_object'];
|
||||||
Log::debug(sprintf('Last paid date is: %s', $return->format('Y-m-d')));
|
Log::debug(sprintf('[b] Last paid date is: %s', $return->format('Y-m-d')));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log::debug(sprintf('Last paid date is: "%s"', $return?->format('Y-m-d')));
|
Log::debug(sprintf('[c] Last paid date is: "%s"', $return?->format('Y-m-d')));
|
||||||
|
|
||||||
return $return;
|
return $return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,26 +45,20 @@ use Override;
|
|||||||
|
|
||||||
class TransactionGroupEnrichment implements EnrichmentInterface
|
class TransactionGroupEnrichment implements EnrichmentInterface
|
||||||
{
|
{
|
||||||
private array $attachmentCount;
|
private array $attachmentCount = [];
|
||||||
private Collection $collection;
|
private Collection $collection;
|
||||||
private readonly array $dateFields;
|
private readonly array $dateFields;
|
||||||
private array $journalIds;
|
private array $journalIds = [];
|
||||||
private array $locations;
|
private array $locations = [];
|
||||||
private array $metaData; // @phpstan-ignore-line
|
private array $metaData = [];
|
||||||
private array $notes; // @phpstan-ignore-line
|
private array $notes = [];
|
||||||
private array $tags;
|
private array $tags = [];
|
||||||
private User $user;
|
private User $user;
|
||||||
private readonly TransactionCurrency $primaryCurrency;
|
private readonly TransactionCurrency $primaryCurrency;
|
||||||
private UserGroup $userGroup;
|
private UserGroup $userGroup;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->notes = [];
|
|
||||||
$this->journalIds = [];
|
|
||||||
$this->tags = [];
|
|
||||||
$this->metaData = [];
|
|
||||||
$this->locations = [];
|
|
||||||
$this->attachmentCount = [];
|
|
||||||
$this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
|
$this->dateFields = ['interest_date', 'book_date', 'process_date', 'due_date', 'payment_date', 'invoice_date'];
|
||||||
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
$this->primaryCurrency = Amount::getPrimaryCurrency();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -110,8 +110,8 @@ class BillDateCalculator
|
|||||||
$currentStart = clone $nextExpectedMatch;
|
$currentStart = clone $nextExpectedMatch;
|
||||||
|
|
||||||
++$loop;
|
++$loop;
|
||||||
if ($loop > 12) {
|
if ($loop > 31) {
|
||||||
Log::debug('Loop is more than 12, so we break.');
|
Log::debug('Loop is more than 31, so we break.');
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ namespace FireflyIII\Support;
|
|||||||
|
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\Preference;
|
use FireflyIII\Models\Preference;
|
||||||
|
use FireflyIII\Support\Singleton\PreferencesSingleton;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Contracts\Encryption\DecryptException;
|
use Illuminate\Contracts\Encryption\DecryptException;
|
||||||
use Illuminate\Contracts\Encryption\EncryptException;
|
use Illuminate\Contracts\Encryption\EncryptException;
|
||||||
@@ -283,6 +284,12 @@ class Preferences
|
|||||||
*/
|
*/
|
||||||
public function lastActivity(): string
|
public function lastActivity(): string
|
||||||
{
|
{
|
||||||
|
$instance = PreferencesSingleton::getInstance();
|
||||||
|
$pref = $instance->getPreference('last_activity');
|
||||||
|
if (null !== $pref) {
|
||||||
|
// Log::debug(sprintf('Found last activity in singleton: %s', $pref));
|
||||||
|
return $pref;
|
||||||
|
}
|
||||||
$lastActivity = microtime();
|
$lastActivity = microtime();
|
||||||
$preference = $this->get('lastActivity', microtime());
|
$preference = $this->get('lastActivity', microtime());
|
||||||
|
|
||||||
@@ -292,13 +299,17 @@ class Preferences
|
|||||||
if (is_array($lastActivity)) {
|
if (is_array($lastActivity)) {
|
||||||
$lastActivity = implode(',', $lastActivity);
|
$lastActivity = implode(',', $lastActivity);
|
||||||
}
|
}
|
||||||
|
$setting = hash('sha256', (string) $lastActivity);
|
||||||
|
$instance->setPreference('last_activity', $setting);
|
||||||
|
|
||||||
return hash('sha256', (string) $lastActivity);
|
return $setting;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mark(): void
|
public function mark(): void
|
||||||
{
|
{
|
||||||
$this->set('lastActivity', microtime());
|
$this->set('lastActivity', microtime());
|
||||||
|
$instance = PreferencesSingleton::getInstance();
|
||||||
|
$instance->setPreference('last_activity', microtime());
|
||||||
Session::forget('first');
|
Session::forget('first');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
41
app/Support/Singleton/PreferencesSingleton.php
Normal file
41
app/Support/Singleton/PreferencesSingleton.php
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace FireflyIII\Support\Singleton;
|
||||||
|
|
||||||
|
class PreferencesSingleton
|
||||||
|
{
|
||||||
|
private static ?PreferencesSingleton $instance = null;
|
||||||
|
|
||||||
|
private array $preferences = [];
|
||||||
|
|
||||||
|
private function __construct()
|
||||||
|
{
|
||||||
|
// Private constructor to prevent direct instantiation.
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getInstance(): self
|
||||||
|
{
|
||||||
|
if (null === self::$instance) {
|
||||||
|
self::$instance = new self();
|
||||||
|
}
|
||||||
|
|
||||||
|
return self::$instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function resetPreferences(): void
|
||||||
|
{
|
||||||
|
$this->preferences = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPreference(string $key, mixed $value): void
|
||||||
|
{
|
||||||
|
$this->preferences[$key] = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPreference(string $key): mixed
|
||||||
|
{
|
||||||
|
return $this->preferences[$key] ?? null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@ use FireflyIII\Models\Transaction;
|
|||||||
use FireflyIII\Models\TransactionCurrency;
|
use FireflyIII\Models\TransactionCurrency;
|
||||||
use FireflyIII\Support\Facades\Amount;
|
use FireflyIII\Support\Facades\Amount;
|
||||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||||
|
use FireflyIII\Support\Singleton\PreferencesSingleton;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
@@ -321,44 +322,54 @@ class Steam
|
|||||||
|
|
||||||
public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
|
public function accountsBalancesOptimized(Collection $accounts, Carbon $date, ?TransactionCurrency $primary = null, ?bool $convertToPrimary = null): array
|
||||||
{
|
{
|
||||||
$result = [];
|
Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String()));
|
||||||
|
$result = [];
|
||||||
$convertToPrimary ??= Amount::convertToPrimary();
|
$convertToPrimary ??= Amount::convertToPrimary();
|
||||||
$primary ??= Amount::getPrimaryCurrency();
|
$primary ??= Amount::getPrimaryCurrency();
|
||||||
$currencies = $this->getCurrencies($accounts);
|
$currencies = $this->getCurrencies($accounts);
|
||||||
|
|
||||||
// balance(s) in all currencies for ALL accounts.
|
// balance(s) in all currencies for ALL accounts.
|
||||||
$array = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
|
$arrayOfSums = Transaction::whereIn('account_id', $accounts->pluck('id')->toArray())
|
||||||
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
->leftJoin('transaction_journals', 'transaction_journals.id', '=', 'transactions.transaction_journal_id')
|
||||||
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
|
->leftJoin('transaction_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
|
||||||
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
|
->where('transaction_journals.date', '<=', $date->format('Y-m-d H:i:s'))
|
||||||
->get(['transactions.account_id', 'transaction_currencies.code', 'transactions.amount'])->toArray()
|
->groupBy(['transactions.account_id', 'transaction_currencies.code'])
|
||||||
|
->get(['transactions.account_id', 'transaction_currencies.code', DB::raw('SUM(transactions.amount) as sum_of_amount')])->toArray()
|
||||||
;
|
;
|
||||||
|
|
||||||
/** @var Account $account */
|
/** @var Account $account */
|
||||||
foreach ($accounts as $account) {
|
foreach ($accounts as $account) {
|
||||||
// filter array back to this account:
|
|
||||||
$filtered = array_filter($array, function ($item) use ($account) {
|
|
||||||
return (int)$item['account_id'] === $account->id;
|
|
||||||
});
|
|
||||||
$currency = $currencies[$account->id];
|
|
||||||
// this array is PER account, so we wait a bit before we change code here.
|
// this array is PER account, so we wait a bit before we change code here.
|
||||||
$return = [
|
$return = [
|
||||||
'pc_balance' => '0',
|
'pc_balance' => '0',
|
||||||
'balance' => '0', // this key is overwritten right away, but I must remember it is always created.
|
'balance' => '0', // this key is overwritten right away, but I must remember it is always created.
|
||||||
];
|
];
|
||||||
|
$currency = $currencies[$account->id];
|
||||||
|
|
||||||
|
// second array
|
||||||
|
$accountSum = array_filter($arrayOfSums, function ($entry) use ($account) {
|
||||||
|
return $entry['account_id'] === $account->id;
|
||||||
|
});
|
||||||
|
if (0 === count($accountSum)) {
|
||||||
|
$result[$account->id] = $return;
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$accountSum = array_values($accountSum)[0];
|
||||||
|
$sumsByCode = [
|
||||||
|
$accountSum['code'] => $accountSum['sum_of_amount'],
|
||||||
|
];
|
||||||
|
|
||||||
// balance(s) in all currencies.
|
|
||||||
$others = $this->groupAndSumTransactions($filtered, 'code', 'amount');
|
|
||||||
// Log::debug('All balances are (joined)', $others);
|
// Log::debug('All balances are (joined)', $others);
|
||||||
// if there is no request to convert, take this as "balance" and "pc_balance".
|
// if there is no request to convert, take this as "balance" and "pc_balance".
|
||||||
$return['balance'] = $others[$currency->code] ?? '0';
|
$return['balance'] = $sumsByCode[$currency->code] ?? '0';
|
||||||
if (!$convertToPrimary) {
|
if (!$convertToPrimary) {
|
||||||
unset($return['pc_balance']);
|
unset($return['pc_balance']);
|
||||||
// Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance']));
|
// Log::debug(sprintf('Set balance to %s, unset pc_balance', $return['balance']));
|
||||||
}
|
}
|
||||||
// if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency.
|
// if there is a request to convert, convert to "pc_balance" and use "balance" for whichever amount is in the primary currency.
|
||||||
if ($convertToPrimary) {
|
if ($convertToPrimary) {
|
||||||
$return['pc_balance'] = $this->convertAllBalances($others, $primary, $date); // todo sum all and convert.
|
$return['pc_balance'] = $this->convertAllBalances($sumsByCode, $primary, $date);
|
||||||
// Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance']));
|
// Log::debug(sprintf('Set pc_balance to %s', $return['pc_balance']));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -377,7 +388,7 @@ class Steam
|
|||||||
$return['balance'] = bcadd($return['balance'], $virtualBalance);
|
$return['balance'] = bcadd($return['balance'], $virtualBalance);
|
||||||
// Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance']));
|
// Log::debug(sprintf('Virtual balance makes the (primary currency) total %s', $return['balance']));
|
||||||
}
|
}
|
||||||
$final = array_merge($return, $others);
|
$final = array_merge($return, $sumsByCode);
|
||||||
$result[$account->id] = $final;
|
$result[$account->id] = $final;
|
||||||
// Log::debug('Final balance is', $final);
|
// Log::debug('Final balance is', $final);
|
||||||
}
|
}
|
||||||
@@ -507,30 +518,27 @@ class Steam
|
|||||||
{
|
{
|
||||||
$total = '0';
|
$total = '0';
|
||||||
$converter = new ExchangeRateConverter();
|
$converter = new ExchangeRateConverter();
|
||||||
|
$singleton = PreferencesSingleton::getInstance();
|
||||||
foreach ($others as $key => $amount) {
|
foreach ($others as $key => $amount) {
|
||||||
$currency = TransactionCurrency::where('code', $key)->first();
|
$preference = $singleton->getPreference($key);
|
||||||
|
$currency = $preference ?? TransactionCurrency::where('code', $key)->first();
|
||||||
if (null === $currency) {
|
if (null === $currency) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$current = $converter->convert($currency, $primary, $date, $amount);
|
if (null === $preference) {
|
||||||
Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current));
|
$singleton->setPreference($key, $currency);
|
||||||
$total = bcadd($current, $total);
|
}
|
||||||
|
$current = $amount;
|
||||||
|
if ($currency->id !== $primary->id) {
|
||||||
|
$current = $converter->convert($currency, $primary, $date, $amount);
|
||||||
|
Log::debug(sprintf('Convert %s %s to %s %s', $currency->code, $amount, $primary->code, $current));
|
||||||
|
}
|
||||||
|
$total = bcadd($current, $total);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $total;
|
return $total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function finalAccountsBalance(Collection $accounts, Carbon $date): array
|
|
||||||
{
|
|
||||||
Log::debug(sprintf('finalAccountsBalance: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
|
||||||
$balances = [];
|
|
||||||
foreach ($accounts as $account) {
|
|
||||||
$balances[$account->id] = $this->finalAccountBalance($account, $date);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $balances;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -108,7 +108,9 @@ class AccountTransformer extends AbstractTransformer
|
|||||||
'type' => strtolower($accountType),
|
'type' => strtolower($accountType),
|
||||||
'account_role' => $accountRole,
|
'account_role' => $accountRole,
|
||||||
|
|
||||||
// TODO object group
|
'object_group_id' => $account->meta['object_group_id'],
|
||||||
|
'object_group_order' => $account->meta['object_group_order'],
|
||||||
|
'object_group_title' => $account->meta['object_group_title'],
|
||||||
|
|
||||||
// currency information, structured for 6.3.0.
|
// currency information, structured for 6.3.0.
|
||||||
'object_has_currency_setting' => $hasCurrencySettings,
|
'object_has_currency_setting' => $hasCurrencySettings,
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ class AttachmentTransformer extends AbstractTransformer
|
|||||||
'updated_at' => $attachment->updated_at->toAtomString(),
|
'updated_at' => $attachment->updated_at->toAtomString(),
|
||||||
'attachable_id' => (string) $attachment->attachable_id,
|
'attachable_id' => (string) $attachment->attachable_id,
|
||||||
'attachable_type' => str_replace('FireflyIII\Models\\', '', $attachment->attachable_type),
|
'attachable_type' => str_replace('FireflyIII\Models\\', '', $attachment->attachable_type),
|
||||||
'md5' => $attachment->md5,
|
|
||||||
'hash' => $attachment->md5,
|
'hash' => $attachment->md5,
|
||||||
'filename' => $attachment->filename,
|
'filename' => $attachment->filename,
|
||||||
'download_url' => route('api.v1.attachments.download', [$attachment->id]),
|
'download_url' => route('api.v1.attachments.download', [$attachment->id]),
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ class BillTransformer extends AbstractTransformer
|
|||||||
'object_group_order' => $bill->meta['object_group_order'],
|
'object_group_order' => $bill->meta['object_group_order'],
|
||||||
'object_group_title' => $bill->meta['object_group_title'],
|
'object_group_title' => $bill->meta['object_group_title'],
|
||||||
|
|
||||||
|
|
||||||
'paid_dates' => $bill->meta['paid_dates'],
|
'paid_dates' => $bill->meta['paid_dates'],
|
||||||
'pay_dates' => $bill->meta['pay_dates'],
|
'pay_dates' => $bill->meta['pay_dates'],
|
||||||
'next_expected_match' => $bill->meta['nem']?->toAtomString(),
|
'next_expected_match' => $bill->meta['nem']?->toAtomString(),
|
||||||
|
|||||||
@@ -64,10 +64,7 @@ class BudgetLimitTransformer extends AbstractTransformer
|
|||||||
public function transform(BudgetLimit $budgetLimit): array
|
public function transform(BudgetLimit $budgetLimit): array
|
||||||
{
|
{
|
||||||
|
|
||||||
$currency = $budgetLimit->transactionCurrency;
|
$currency = $budgetLimit->meta['currency'];
|
||||||
if (null === $currency) {
|
|
||||||
$currency = $this->primaryCurrency;
|
|
||||||
}
|
|
||||||
$amount = Steam::bcround($budgetLimit->amount, $currency->decimal_places);
|
$amount = Steam::bcround($budgetLimit->amount, $currency->decimal_places);
|
||||||
$pcAmount = null;
|
$pcAmount = null;
|
||||||
if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) {
|
if ($this->convertToPrimary && $currency->id === $this->primaryCurrency->id) {
|
||||||
|
|||||||
@@ -86,8 +86,9 @@ class BudgetTransformer extends AbstractTransformer
|
|||||||
'notes' => $budget->meta['notes'],
|
'notes' => $budget->meta['notes'],
|
||||||
'auto_budget_type' => $abType,
|
'auto_budget_type' => $abType,
|
||||||
'auto_budget_period' => $abPeriod,
|
'auto_budget_period' => $abPeriod,
|
||||||
|
'object_group_id' => $budget->meta['object_group_id'],
|
||||||
// TODO object group
|
'object_group_order' => $budget->meta['object_group_order'],
|
||||||
|
'object_group_title' => $budget->meta['object_group_title'],
|
||||||
|
|
||||||
// new currency settings.
|
// new currency settings.
|
||||||
'object_has_currency_setting' => null !== $budget->meta['currency'],
|
'object_has_currency_setting' => null !== $budget->meta['currency'],
|
||||||
@@ -105,8 +106,8 @@ class BudgetTransformer extends AbstractTransformer
|
|||||||
|
|
||||||
'auto_budget_amount' => $abAmount,
|
'auto_budget_amount' => $abAmount,
|
||||||
'pc_auto_budget_amount' => $abPrimary,
|
'pc_auto_budget_amount' => $abPrimary,
|
||||||
'spent' => $this->beautify($budget->meta['spent']),
|
'spent' => null === $budget->meta['spent'] ? null : $this->beautify($budget->meta['spent']),
|
||||||
'pc_spent' => $this->beautify($budget->meta['pc_spent']),
|
'pc_spent' => null === $budget->meta['pc_spent'] ? null : $this->beautify($budget->meta['pc_spent']),
|
||||||
'links' => [
|
'links' => [
|
||||||
[
|
[
|
||||||
'rel' => 'self',
|
'rel' => 'self',
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class RecurrenceTransformer extends AbstractTransformer
|
|||||||
'nr_of_repetitions' => $reps,
|
'nr_of_repetitions' => $reps,
|
||||||
'notes' => $recurrence->meta['notes'],
|
'notes' => $recurrence->meta['notes'],
|
||||||
'repetitions' => $recurrence->meta['repetitions'],
|
'repetitions' => $recurrence->meta['repetitions'],
|
||||||
'new_transactions' => $recurrence->meta['transactions'],
|
'transactions' => $recurrence->meta['transactions'],
|
||||||
'links' => [
|
'links' => [
|
||||||
[
|
[
|
||||||
'rel' => 'self',
|
'rel' => 'self',
|
||||||
|
|||||||
47
changelog.md
47
changelog.md
@@ -3,6 +3,53 @@
|
|||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
|
## 6.3.0 - 2025-08-xx
|
||||||
|
|
||||||
|
> ⚠️ Firefly III v6.3.0 introduces a lot of API changes that deal with multi-currency support. Make sure your beloved apps are updated to support this.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- [Issue 6836](https://github.com/firefly-iii/firefly-iii/issues/6836) (Send email about coming/past-due bills) reported by @elgatho
|
||||||
|
- [Issue 9640](https://github.com/firefly-iii/firefly-iii/issues/9640) (UI Improvements for Rules) reported by @siriuspal
|
||||||
|
- [Issue 9650](https://github.com/firefly-iii/firefly-iii/issues/9650) (Extra line in bills overview) reported by @poudenes
|
||||||
|
- Add Arabic as language, translations follow.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- [Issue 10071](https://github.com/firefly-iii/firefly-iii/issues/10071) (Allow toggling password field to text) reported by @ragul-engg
|
||||||
|
- Renamed all instances of "default" and "native" currency to "primary" currency. This influences translations and API endpoints. The database is not changed because that's difficult to do reliably.
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Any API-field called `default_*` or `native_*`. Use `primary_*` instead.
|
||||||
|
- All v2 endpoints.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- [Issue 9849](https://github.com/firefly-iii/firefly-iii/issues/9849) ("Display native amounts" not taken into account in report's pie charts) reported by @polter-rnd
|
||||||
|
- [Issue 10565](https://github.com/firefly-iii/firefly-iii/issues/10565) (Unable to delete reconciliation transaction) reported by @berta24
|
||||||
|
- [Issue 10600](https://github.com/firefly-iii/firefly-iii/issues/10600) (Show attachmen iccon when listing tranactions) reported by @JcMinarro
|
||||||
|
- [Discussion 10618](https://github.com/orgs/firefly-iii/discussions/10618) (Starting balance includes transactions that occur at 00:00 on the 1st of month) started by @jteez
|
||||||
|
- [Issue 10646](https://github.com/firefly-iii/firefly-iii/issues/10646) (Webhooks fire even if disabled) reported by @lvu
|
||||||
|
- [Issue 10656](https://github.com/firefly-iii/firefly-iii/issues/10656) (spent info "per day" shows the period total) reported by @frank-bg
|
||||||
|
- [Issue 10678](https://github.com/firefly-iii/firefly-iii/issues/10678) (Transactions from asset to liability account do not appear on category reports.) reported by @slackspace-io
|
||||||
|
- [Issue 10687](https://github.com/firefly-iii/firefly-iii/issues/10687) (Creating new Piggy Bank via API fails (Unexpected empty currency)) reported by @Madnex
|
||||||
|
- [Issue 10700](https://github.com/firefly-iii/firefly-iii/issues/10700) (Setting financial year date is inconsistent due to timezone calculations) reported by @AgeManning
|
||||||
|
- [Issue 10702](https://github.com/firefly-iii/firefly-iii/issues/10702) (Wrong order of months in category report) reported by @kapuett
|
||||||
|
- [Issue 10703](https://github.com/firefly-iii/firefly-iii/issues/10703) (Fire more than 5 webhooks in batch or through the cron job, and document it.) reported by @JC5
|
||||||
|
- [Issue 10704](https://github.com/firefly-iii/firefly-iii/issues/10704) (Some triggers with rule automation seems to have an issue) reported by @Alienlog
|
||||||
|
- [Issue 10706](https://github.com/firefly-iii/firefly-iii/issues/10706) (Add KRW in Default Currency List) reported by @readingsnail
|
||||||
|
- [Issue 10708](https://github.com/firefly-iii/firefly-iii/issues/10708) (Incomplete display of a rule when a trigger negates "description caontains") reported by @dethegeek
|
||||||
|
- [Issue 10709](https://github.com/firefly-iii/firefly-iii/issues/10709) (has_any_external_id search parameter invalid) reported by @Alienlog
|
||||||
|
- Tag overview will no longer search for tags dated < 1970.
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
- All remaining API v2 endpoints are deprecated and removed in favour of the API v1 endpoints.
|
||||||
|
- All API read endpoints now support multi-currency. Fields such as the balance and amount fields will also be available as `pc_*`-fields. Objects with currency information also come with new `primary_currency_*` fields.
|
||||||
|
- All API read endpoints are DB optimized and should be faster.
|
||||||
|
- All documentation should be in sync again.
|
||||||
|
- [More info in the docs](https://docs.firefly-iii.org/references/firefly-iii/api/).
|
||||||
|
|
||||||
## 6.2.21 - 2025-07-18
|
## 6.2.21 - 2025-07-18
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
113
composer.lock
generated
113
composer.lock
generated
@@ -586,33 +586,32 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/inflector",
|
"name": "doctrine/inflector",
|
||||||
"version": "2.0.10",
|
"version": "2.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/doctrine/inflector.git",
|
"url": "https://github.com/doctrine/inflector.git",
|
||||||
"reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc"
|
"reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc",
|
"url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b",
|
||||||
"reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc",
|
"reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0"
|
"php": "^7.2 || ^8.0"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"doctrine/coding-standard": "^11.0",
|
"doctrine/coding-standard": "^12.0 || ^13.0",
|
||||||
"phpstan/phpstan": "^1.8",
|
"phpstan/phpstan": "^1.12 || ^2.0",
|
||||||
"phpstan/phpstan-phpunit": "^1.1",
|
"phpstan/phpstan-phpunit": "^1.4 || ^2.0",
|
||||||
"phpstan/phpstan-strict-rules": "^1.3",
|
"phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
|
||||||
"phpunit/phpunit": "^8.5 || ^9.5",
|
"phpunit/phpunit": "^8.5 || ^12.2"
|
||||||
"vimeo/psalm": "^4.25 || ^5.4"
|
|
||||||
},
|
},
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
|
"Doctrine\\Inflector\\": "src"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
@@ -657,7 +656,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/doctrine/inflector/issues",
|
"issues": "https://github.com/doctrine/inflector/issues",
|
||||||
"source": "https://github.com/doctrine/inflector/tree/2.0.10"
|
"source": "https://github.com/doctrine/inflector/tree/2.1.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -673,7 +672,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2024-02-18T20:23:39+00:00"
|
"time": "2025-08-10T19:31:58+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "doctrine/lexer",
|
"name": "doctrine/lexer",
|
||||||
@@ -939,16 +938,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "filp/whoops",
|
"name": "filp/whoops",
|
||||||
"version": "2.18.3",
|
"version": "2.18.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/filp/whoops.git",
|
"url": "https://github.com/filp/whoops.git",
|
||||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5"
|
"reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5",
|
"url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d",
|
||||||
"reference": "59a123a3d459c5a23055802237cb317f609867e5",
|
"reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -998,7 +997,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/filp/whoops/issues",
|
"issues": "https://github.com/filp/whoops/issues",
|
||||||
"source": "https://github.com/filp/whoops/tree/2.18.3"
|
"source": "https://github.com/filp/whoops/tree/2.18.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1006,7 +1005,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-16T00:02:10+00:00"
|
"time": "2025-08-08T12:00:00+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
@@ -1879,16 +1878,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/framework",
|
"name": "laravel/framework",
|
||||||
"version": "v12.21.0",
|
"version": "v12.22.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/laravel/framework.git",
|
"url": "https://github.com/laravel/framework.git",
|
||||||
"reference": "ac8c4e73bf1b5387b709f7736d41427e6af1c93b"
|
"reference": "d33ee45184126f32f593d4b809a846ed88a1dc43"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/laravel/framework/zipball/ac8c4e73bf1b5387b709f7736d41427e6af1c93b",
|
"url": "https://api.github.com/repos/laravel/framework/zipball/d33ee45184126f32f593d4b809a846ed88a1dc43",
|
||||||
"reference": "ac8c4e73bf1b5387b709f7736d41427e6af1c93b",
|
"reference": "d33ee45184126f32f593d4b809a846ed88a1dc43",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2090,7 +2089,7 @@
|
|||||||
"issues": "https://github.com/laravel/framework/issues",
|
"issues": "https://github.com/laravel/framework/issues",
|
||||||
"source": "https://github.com/laravel/framework"
|
"source": "https://github.com/laravel/framework"
|
||||||
},
|
},
|
||||||
"time": "2025-07-22T15:41:55+00:00"
|
"time": "2025-08-08T13:58:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "laravel/passport",
|
"name": "laravel/passport",
|
||||||
@@ -10347,16 +10346,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "driftingly/rector-laravel",
|
"name": "driftingly/rector-laravel",
|
||||||
"version": "2.0.5",
|
"version": "2.0.6",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/driftingly/rector-laravel.git",
|
"url": "https://github.com/driftingly/rector-laravel.git",
|
||||||
"reference": "ac61de4f267c23249d175d7fc9149fd01528567d"
|
"reference": "5be95811801fc06126dd844beaeb6a41721ba3d3"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/ac61de4f267c23249d175d7fc9149fd01528567d",
|
"url": "https://api.github.com/repos/driftingly/rector-laravel/zipball/5be95811801fc06126dd844beaeb6a41721ba3d3",
|
||||||
"reference": "ac61de4f267c23249d175d7fc9149fd01528567d",
|
"reference": "5be95811801fc06126dd844beaeb6a41721ba3d3",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10376,9 +10375,9 @@
|
|||||||
"description": "Rector upgrades rules for Laravel Framework",
|
"description": "Rector upgrades rules for Laravel Framework",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/driftingly/rector-laravel/issues",
|
"issues": "https://github.com/driftingly/rector-laravel/issues",
|
||||||
"source": "https://github.com/driftingly/rector-laravel/tree/2.0.5"
|
"source": "https://github.com/driftingly/rector-laravel/tree/2.0.6"
|
||||||
},
|
},
|
||||||
"time": "2025-05-14T17:30:41+00:00"
|
"time": "2025-08-08T22:10:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "fakerphp/faker",
|
"name": "fakerphp/faker",
|
||||||
@@ -11618,16 +11617,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "12.3.0",
|
"version": "12.3.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "264da860d6fe0d00582355a6ecbbf7ae57b44895"
|
"reference": "ac6952c92e8a66ee5698cf81f421120ff64c8d0f"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/264da860d6fe0d00582355a6ecbbf7ae57b44895",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ac6952c92e8a66ee5698cf81f421120ff64c8d0f",
|
||||||
"reference": "264da860d6fe0d00582355a6ecbbf7ae57b44895",
|
"reference": "ac6952c92e8a66ee5698cf81f421120ff64c8d0f",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11637,7 +11636,7 @@
|
|||||||
"ext-mbstring": "*",
|
"ext-mbstring": "*",
|
||||||
"ext-xml": "*",
|
"ext-xml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"myclabs/deep-copy": "^1.13.3",
|
"myclabs/deep-copy": "^1.13.4",
|
||||||
"phar-io/manifest": "^2.0.4",
|
"phar-io/manifest": "^2.0.4",
|
||||||
"phar-io/version": "^3.2.1",
|
"phar-io/version": "^3.2.1",
|
||||||
"php": ">=8.3",
|
"php": ">=8.3",
|
||||||
@@ -11647,13 +11646,13 @@
|
|||||||
"phpunit/php-text-template": "^5.0.0",
|
"phpunit/php-text-template": "^5.0.0",
|
||||||
"phpunit/php-timer": "^8.0.0",
|
"phpunit/php-timer": "^8.0.0",
|
||||||
"sebastian/cli-parser": "^4.0.0",
|
"sebastian/cli-parser": "^4.0.0",
|
||||||
"sebastian/comparator": "^7.1.0",
|
"sebastian/comparator": "^7.1.1",
|
||||||
"sebastian/diff": "^7.0.0",
|
"sebastian/diff": "^7.0.0",
|
||||||
"sebastian/environment": "^8.0.2",
|
"sebastian/environment": "^8.0.2",
|
||||||
"sebastian/exporter": "^7.0.0",
|
"sebastian/exporter": "^7.0.0",
|
||||||
"sebastian/global-state": "^8.0.0",
|
"sebastian/global-state": "^8.0.0",
|
||||||
"sebastian/object-enumerator": "^7.0.0",
|
"sebastian/object-enumerator": "^7.0.0",
|
||||||
"sebastian/type": "^6.0.2",
|
"sebastian/type": "^6.0.3",
|
||||||
"sebastian/version": "^6.0.0",
|
"sebastian/version": "^6.0.0",
|
||||||
"staabm/side-effects-detector": "^1.0.5"
|
"staabm/side-effects-detector": "^1.0.5"
|
||||||
},
|
},
|
||||||
@@ -11695,7 +11694,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
"security": "https://github.com/sebastianbergmann/phpunit/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.0"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/12.3.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11719,7 +11718,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-08-01T05:14:47+00:00"
|
"time": "2025-08-10T08:36:39+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "rector/rector",
|
"name": "rector/rector",
|
||||||
@@ -11840,16 +11839,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/comparator",
|
"name": "sebastian/comparator",
|
||||||
"version": "7.1.0",
|
"version": "7.1.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||||
"reference": "03d905327dccc0851c9a08d6a979dfc683826b6f"
|
"reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f",
|
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1a7c2bce03a13a457ed3c975dfd331b3b4b133aa",
|
||||||
"reference": "03d905327dccc0851c9a08d6a979dfc683826b6f",
|
"reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -11908,7 +11907,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
|
"security": "https://github.com/sebastianbergmann/comparator/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/comparator/tree/7.1.0"
|
"source": "https://github.com/sebastianbergmann/comparator/tree/7.1.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -11928,7 +11927,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-17T07:41:58+00:00"
|
"time": "2025-08-10T08:50:08+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/complexity",
|
"name": "sebastian/complexity",
|
||||||
@@ -12509,16 +12508,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/type",
|
"name": "sebastian/type",
|
||||||
"version": "6.0.2",
|
"version": "6.0.3",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/type.git",
|
"url": "https://github.com/sebastianbergmann/type.git",
|
||||||
"reference": "1d7cd6e514384c36d7a390347f57c385d4be6069"
|
"reference": "e549163b9760b8f71f191651d22acf32d56d6d4d"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069",
|
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d",
|
||||||
"reference": "1d7cd6e514384c36d7a390347f57c385d4be6069",
|
"reference": "e549163b9760b8f71f191651d22acf32d56d6d4d",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -12554,15 +12553,27 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/type/issues",
|
"issues": "https://github.com/sebastianbergmann/type/issues",
|
||||||
"security": "https://github.com/sebastianbergmann/type/security/policy",
|
"security": "https://github.com/sebastianbergmann/type/security/policy",
|
||||||
"source": "https://github.com/sebastianbergmann/type/tree/6.0.2"
|
"source": "https://github.com/sebastianbergmann/type/tree/6.0.3"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"url": "https://github.com/sebastianbergmann",
|
"url": "https://github.com/sebastianbergmann",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://liberapay.com/sebastianbergmann",
|
||||||
|
"type": "liberapay"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://thanks.dev/u/gh/sebastianbergmann",
|
||||||
|
"type": "thanks_dev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "https://tidelift.com/funding/github/packagist/sebastian/type",
|
||||||
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-03-18T13:37:31+00:00"
|
"time": "2025-08-09T06:57:12+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/version",
|
"name": "sebastian/version",
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ return [
|
|||||||
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
|
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
|
||||||
// see cer.php for exchange rates feature flag.
|
// see cer.php for exchange rates feature flag.
|
||||||
],
|
],
|
||||||
'version' => 'develop/2025-08-07',
|
'version' => 'develop/2025-08-11',
|
||||||
'build_time' => 1754540556,
|
'build_time' => 1754883282,
|
||||||
'api_version' => '2.1.0', // field is no longer used.
|
'api_version' => '2.1.0', // field is no longer used.
|
||||||
'db_version' => 26,
|
'db_version' => 26,
|
||||||
|
|
||||||
|
|||||||
48
package-lock.json
generated
48
package-lock.json
generated
@@ -3148,9 +3148,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "24.2.0",
|
"version": "24.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
|
||||||
"integrity": "sha512-3xyG3pMCq3oYCNg7/ZP+E1ooTaGB4cG8JWRsqqOYQdbWNY4zbaV0Ennrd7stjiJEFZCaybcIgpTjJWHRfBSIDw==",
|
"integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -4326,9 +4326,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/browserslist": {
|
"node_modules/browserslist": {
|
||||||
"version": "4.25.1",
|
"version": "4.25.2",
|
||||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz",
|
||||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
"integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4346,8 +4346,8 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"caniuse-lite": "^1.0.30001726",
|
"caniuse-lite": "^1.0.30001733",
|
||||||
"electron-to-chromium": "^1.5.173",
|
"electron-to-chromium": "^1.5.199",
|
||||||
"node-releases": "^2.0.19",
|
"node-releases": "^2.0.19",
|
||||||
"update-browserslist-db": "^1.1.3"
|
"update-browserslist-db": "^1.1.3"
|
||||||
},
|
},
|
||||||
@@ -4486,9 +4486,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/caniuse-lite": {
|
"node_modules/caniuse-lite": {
|
||||||
"version": "1.0.30001731",
|
"version": "1.0.30001734",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz",
|
||||||
"integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==",
|
"integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5700,9 +5700,9 @@
|
|||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/electron-to-chromium": {
|
"node_modules/electron-to-chromium": {
|
||||||
"version": "1.5.198",
|
"version": "1.5.199",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.198.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.199.tgz",
|
||||||
"integrity": "sha512-G5COfnp3w+ydVu80yprgWSfmfQaYRh9DOxfhAxstLyetKaLyl55QrNjx8C38Pc/C+RaDmb1M0Lk8wPEMQ+bGgQ==",
|
"integrity": "sha512-3gl0S7zQd88kCAZRO/DnxtBKuhMO4h0EaQIN3YgZfV6+pW+5+bf2AdQeHNESCoaQqo/gjGVYEf2YM4O5HJQqpQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
@@ -7052,9 +7052,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/i18next": {
|
"node_modules/i18next": {
|
||||||
"version": "25.3.2",
|
"version": "25.3.4",
|
||||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.4.tgz",
|
||||||
"integrity": "sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==",
|
"integrity": "sha512-AHklEYFLiRRxW1Cb6zE9lfnEtYvsydRC8nRS3RSKGX3zCqZ8nLZwMaUsrb80YuccPNv2RNokDL8LkTNnp+6mDw==",
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -7838,9 +7838,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/launch-editor": {
|
"node_modules/launch-editor": {
|
||||||
"version": "2.11.0",
|
"version": "2.11.1",
|
||||||
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.0.tgz",
|
"resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.11.1.tgz",
|
||||||
"integrity": "sha512-R/PIF14L6e2eHkhvQPu7jDRCr0msfCYCxbYiLgkkAGi0dVPWuM+RrsPu0a5dpuNe0KWGL3jpAkOlv53xGfPheQ==",
|
"integrity": "sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -11524,9 +11524,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "7.0.6",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.1.tgz",
|
||||||
"integrity": "sha512-MHFiOENNBd+Bd9uvc8GEsIzdkn1JxMmEeYX35tI3fv0sJBUTfW5tQsoaOwuY4KhBI09A3dUJ/DXf2yxPVPUceg==",
|
"integrity": "sha512-yJ+Mp7OyV+4S+afWo+QyoL9jFWD11QFH0i5i7JypnfTcA1rmgxCbiA8WwAICDEtZ1Z1hzrVhN8R8rGTqkTY8ZQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -11534,7 +11534,7 @@
|
|||||||
"fdir": "^6.4.6",
|
"fdir": "^6.4.6",
|
||||||
"picomatch": "^4.0.3",
|
"picomatch": "^4.0.3",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"rollup": "^4.40.0",
|
"rollup": "^4.43.0",
|
||||||
"tinyglobby": "^0.2.14"
|
"tinyglobby": "^0.2.14"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"administrations_index_menu": "Administraciones financieras",
|
"administrations_index_menu": "Administraciones financieras",
|
||||||
"expires_at": "Expira el",
|
"expires_at": "Expira el",
|
||||||
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
|
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
|
||||||
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
|
"administration_currency_form_help": "Puede tomar m\u00e1s tiempo para que la p\u00e1gina se cargue si cambia la moneda principal, porque puede que la transacci\u00f3n necesite ser convertida a su (nueva) moneda principal.",
|
||||||
"administrations_page_edit_sub_title_js": "Editar administraci\u00f3n financiera \"{title}\"",
|
"administrations_page_edit_sub_title_js": "Editar administraci\u00f3n financiera \"{title}\"",
|
||||||
"table": "Mesa",
|
"table": "Mesa",
|
||||||
"welcome_back": "\u00bfQu\u00e9 est\u00e1 pasando?",
|
"welcome_back": "\u00bfQu\u00e9 est\u00e1 pasando?",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"url": "URL",
|
"url": "URL",
|
||||||
"active": "Activo",
|
"active": "Activo",
|
||||||
"interest_date": "Fecha de inter\u00e9s",
|
"interest_date": "Fecha de inter\u00e9s",
|
||||||
"administration_currency": "Primary currency",
|
"administration_currency": "Moneda Principal",
|
||||||
"title": "T\u00edtulo",
|
"title": "T\u00edtulo",
|
||||||
"date": "Fecha",
|
"date": "Fecha",
|
||||||
"book_date": "Fecha de registro",
|
"book_date": "Fecha de registro",
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
"list": {
|
"list": {
|
||||||
"title": "T\u00edtulo",
|
"title": "T\u00edtulo",
|
||||||
"active": "\u00bfEst\u00e1 Activo?",
|
"active": "\u00bfEst\u00e1 Activo?",
|
||||||
"primary_currency": "Primary currency",
|
"primary_currency": "Moneda principal",
|
||||||
"trigger": "Disparador",
|
"trigger": "Disparador",
|
||||||
"response": "Respuesta",
|
"response": "Respuesta",
|
||||||
"delivery": "Entrega",
|
"delivery": "Entrega",
|
||||||
|
|||||||
@@ -3,8 +3,8 @@
|
|||||||
"administrations_page_title": "Administrations financi\u00e8res",
|
"administrations_page_title": "Administrations financi\u00e8res",
|
||||||
"administrations_index_menu": "Administrations financi\u00e8res",
|
"administrations_index_menu": "Administrations financi\u00e8res",
|
||||||
"expires_at": "Expire le",
|
"expires_at": "Expire le",
|
||||||
"temp_administrations_introduction": "Firefly III will soon get the ability to manage multiple financial administrations. Right now, you only have the one. You can set the title of this administration and its primary currency. This replaces the previous setting where you would set your \"default currency\". This setting is now tied to the financial administration and can be different per administration.",
|
"temp_administrations_introduction": "Firefly III aura bient\u00f4t la possibilit\u00e9 de g\u00e9rer plusieurs administrations financi\u00e8res. Pour le moment, vous n'en avez qu'une. Vous pouvez d\u00e9finir le titre de cette administration et de sa devise principale. Cela remplace le param\u00e8tre pr\u00e9c\u00e9dent o\u00f9 vous d\u00e9finissiez votre \"devise par d\u00e9faut\". Ce param\u00e8tre est d\u00e9sormais li\u00e9 \u00e0 l'administration financi\u00e8re et peut \u00eatre diff\u00e9rent par administration.",
|
||||||
"administration_currency_form_help": "It may take a long time for the page to load if you change the primary currency because transaction may need to be converted to your (new) primary currency.",
|
"administration_currency_form_help": "La page peut mettre longtemps \u00e0 charger si vous modifiez la devise principale, car des op\u00e9rations peuvent n\u00e9cessiter une conversion vers votre (nouvelle) devise principale.",
|
||||||
"administrations_page_edit_sub_title_js": "Modifier l'administration financi\u00e8re \"{title}\"",
|
"administrations_page_edit_sub_title_js": "Modifier l'administration financi\u00e8re \"{title}\"",
|
||||||
"table": "Tableau",
|
"table": "Tableau",
|
||||||
"welcome_back": "Quoi de neuf ?",
|
"welcome_back": "Quoi de neuf ?",
|
||||||
@@ -154,7 +154,7 @@
|
|||||||
"url": "Liens",
|
"url": "Liens",
|
||||||
"active": "Actif",
|
"active": "Actif",
|
||||||
"interest_date": "Date de valeur (int\u00e9r\u00eats)",
|
"interest_date": "Date de valeur (int\u00e9r\u00eats)",
|
||||||
"administration_currency": "Primary currency",
|
"administration_currency": "Devise principale",
|
||||||
"title": "Titre",
|
"title": "Titre",
|
||||||
"date": "Date",
|
"date": "Date",
|
||||||
"book_date": "Date d'enregistrement",
|
"book_date": "Date d'enregistrement",
|
||||||
@@ -174,7 +174,7 @@
|
|||||||
"list": {
|
"list": {
|
||||||
"title": "Titre",
|
"title": "Titre",
|
||||||
"active": "Actif ?",
|
"active": "Actif ?",
|
||||||
"primary_currency": "Primary currency",
|
"primary_currency": "Devise principale",
|
||||||
"trigger": "D\u00e9clencheur",
|
"trigger": "D\u00e9clencheur",
|
||||||
"response": "R\u00e9ponse",
|
"response": "R\u00e9ponse",
|
||||||
"delivery": "Distribution",
|
"delivery": "Distribution",
|
||||||
|
|||||||
@@ -138,6 +138,14 @@ return [
|
|||||||
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
|
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
|
||||||
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
|
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
|
||||||
|
|
||||||
|
// subscription is overdue.
|
||||||
|
'subscriptions_overdue_subject_multi' => 'You have :count subscriptions that are overdue to be paid',
|
||||||
|
'subscriptions_overdue_subject_single' => 'You have a subscription that is overdue to be paid',
|
||||||
|
'subscriptions_overdue_warning_intro_single' => 'You have one subscription that is overdue to be paid. At the following date(s) a payment was expected, but it has not yet arrived.',
|
||||||
|
'subscriptions_overdue_warning_intro_multi' => 'You have :count subscription(s) that are overdue to be paid. At the following date(s) a payment was expected, but it has not yet arrived.',
|
||||||
|
'subscriptions_overdue_please_action_single' => 'Perhaps you have simply not linked a transaction to this subscription. In that case, please do so. You will NOT get another warning about this overdue subscription. A new warning will be sent out for the NEXT due payment.',
|
||||||
|
'subscriptions_overdue_please_action_multi' => 'Perhaps you have simply not linked a transaction to these subscriptions. In that case, please do so. You will NOT get another warning about these overdue subscriptions. A new warning will be sent out for the NEXT due payments.',
|
||||||
|
'subscriptions_overdue_outro' => 'If you believe this message is wrong, please contact the Firefly III developer. Thank you for using Firefly III.',
|
||||||
// bill warning
|
// bill warning
|
||||||
'bill_warning_subject_end_date' => 'Your subscription ":name" is due to end in :diff days',
|
'bill_warning_subject_end_date' => 'Your subscription ":name" is due to end in :diff days',
|
||||||
'bill_warning_subject_now_end_date' => 'Your subscription ":name" is due to end TODAY',
|
'bill_warning_subject_now_end_date' => 'Your subscription ":name" is due to end TODAY',
|
||||||
|
|||||||
@@ -1864,6 +1864,7 @@ return [
|
|||||||
'remove_budgeted_amount' => 'Remove budgeted amount in :currency',
|
'remove_budgeted_amount' => 'Remove budgeted amount in :currency',
|
||||||
|
|
||||||
// bills:
|
// bills:
|
||||||
|
'left_to_pay_active_bills' => 'active, expected and not yet paid subscriptions',
|
||||||
'left_to_pay_lc' => 'left to pay',
|
'left_to_pay_lc' => 'left to pay',
|
||||||
'less_than_expected' => 'less than expected',
|
'less_than_expected' => 'less than expected',
|
||||||
'more_than_expected' => 'more than expected',
|
'more_than_expected' => 'more than expected',
|
||||||
@@ -1874,6 +1875,7 @@ return [
|
|||||||
'subscr_expected_x_times' => 'Expect to pay {{amount}} {{times}} times this period',
|
'subscr_expected_x_times' => 'Expect to pay {{amount}} {{times}} times this period',
|
||||||
'not_or_not_yet' => 'Not (yet)',
|
'not_or_not_yet' => 'Not (yet)',
|
||||||
'visit_bill' => 'Visit subscription ":name" at Firefly III',
|
'visit_bill' => 'Visit subscription ":name" at Firefly III',
|
||||||
|
'visit_bills' => 'Visit subscriptions at Firefly III',
|
||||||
'match_between_amounts' => 'Subscription matches transactions between :low and :high.',
|
'match_between_amounts' => 'Subscription matches transactions between :low and :high.',
|
||||||
'running_again_loss' => 'Previously linked transactions to this subscription may lose their connection, if they (no longer) match the rule(s).',
|
'running_again_loss' => 'Previously linked transactions to this subscription may lose their connection, if they (no longer) match the rule(s).',
|
||||||
'bill_related_rules' => 'Rules related to this subscription',
|
'bill_related_rules' => 'Rules related to this subscription',
|
||||||
@@ -2331,6 +2333,7 @@ return [
|
|||||||
|
|
||||||
|
|
||||||
// reports:
|
// reports:
|
||||||
|
'quick_link_needs_accounts' => 'In order to generate reports, you need to add at least one asset account to Firefly III.',
|
||||||
'report_default' => 'Default financial report between :start and :end',
|
'report_default' => 'Default financial report between :start and :end',
|
||||||
'report_audit' => 'Transaction history overview between :start and :end',
|
'report_audit' => 'Transaction history overview between :start and :end',
|
||||||
'report_category' => 'Category report between :start and :end',
|
'report_category' => 'Category report between :start and :end',
|
||||||
|
|||||||
@@ -56,8 +56,12 @@
|
|||||||
<div class="input-group-text"> <em class="fa-solid fa-user"></em> </div>
|
<div class="input-group-text"> <em class="fa-solid fa-user"></em> </div>
|
||||||
</div>
|
</div>
|
||||||
@endif
|
@endif
|
||||||
<div class="input-group mb-3"> <input type="password" name="password" class="form-control" placeholder="{{ trans('form.password') }}" @if(true===$IS_DEMO_SITE)value="{{ $DEMO_PASSWORD }}"@endif autocomplete="current-password">
|
<div class="input-group mb-3">
|
||||||
<div class="input-group-text"> <em class="fa-solid fa-lock"></em> </div>
|
<input type="password" id="password" name="password" class="form-control" placeholder="{{ trans('form.password') }}" @if(true===$IS_DEMO_SITE)value="{{ $DEMO_PASSWORD }}"@endif autocomplete="current-password">
|
||||||
|
<div class="input-group-text">
|
||||||
|
<em class="fa-solid fa-lock"></em>
|
||||||
|
<i class="fa-solid fa-eye-slash fa-eye" id="togglePassword"></i>
|
||||||
|
</div>
|
||||||
</div> <!--begin::Row-->
|
</div> <!--begin::Row-->
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
@@ -80,3 +84,21 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
@endsection
|
@endsection
|
||||||
|
@section('scripts')
|
||||||
|
<script nonce="{{ $JS_NONCE }}">
|
||||||
|
const togglePassword = document.querySelector('#togglePassword');
|
||||||
|
const password = document.querySelector('#password');
|
||||||
|
togglePassword.addEventListener('click', () => {
|
||||||
|
const type = password.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||||
|
if('text' === type) {
|
||||||
|
togglePassword.classList.add('fa-eye');
|
||||||
|
togglePassword.classList.remove('fa-eye-slash');
|
||||||
|
}
|
||||||
|
if('password' === type) {
|
||||||
|
togglePassword.classList.add('fa-eye-slash');
|
||||||
|
togglePassword.classList.remove('fa-eye');
|
||||||
|
}
|
||||||
|
password.setAttribute('type', type);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
|
|||||||
@@ -158,8 +158,8 @@
|
|||||||
<td style="width:33%;">{{ 'amount'|_ }}</td>
|
<td style="width:33%;">{{ 'amount'|_ }}</td>
|
||||||
<td>
|
<td>
|
||||||
{{ formatAmountBySymbol(limit.amount, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
|
{{ formatAmountBySymbol(limit.amount, limit.transactionCurrency.symbol, limit.transactionCurrency.decimal_places) }}
|
||||||
{% if convertToPrimary and 0 != limit.pc_amount %}
|
{% if convertToPrimary and null != limit.native_amount %}
|
||||||
({{ formatAmountBySymbol(limit.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
({{ formatAmountBySymbol(limit.native_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
@component('mail::message')
|
||||||
|
@if(1 === $count)
|
||||||
|
{{ trans('email.subscriptions_overdue_warning_intro_single') }}
|
||||||
|
@endif
|
||||||
|
@if(1 !== $count)
|
||||||
|
{{ trans('email.subscriptions_overdue_warning_intro_multi', ['count' => $count]) }}
|
||||||
|
@endif
|
||||||
|
@foreach($info as $row)
|
||||||
|
- {{ $row['bill']->name }}:
|
||||||
|
@foreach($row['pay_dates'] as $date)
|
||||||
|
- {{ $date }}
|
||||||
|
@endforeach
|
||||||
|
@endforeach
|
||||||
|
|
||||||
|
@if(1 === $count)
|
||||||
|
{{ trans('email.subscriptions_overdue_please_action_single') }}
|
||||||
|
@endif
|
||||||
|
@if(1 !== $count)
|
||||||
|
{{ trans('email.subscriptions_overdue_please_action_multi', ['count' => $count]) }}
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{ trans('email.subscriptions_overdue_outro') }}
|
||||||
|
|
||||||
|
@endcomponent
|
||||||
@@ -189,6 +189,21 @@
|
|||||||
<td class="hidden-sm hidden-xs"> </td><!-- repeats -->
|
<td class="hidden-sm hidden-xs"> </td><!-- repeats -->
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% if '0' != sum.total_left_to_pay %}
|
||||||
|
<tr>
|
||||||
|
<td class="hidden-sm hidden-xs"> </td> <!-- handle -->
|
||||||
|
<td class="hidden-sm hidden-xs"> </td> <!-- buttons -->
|
||||||
|
<td colspan="2" style="text-align: right;"> <!-- title -->
|
||||||
|
<small>{{ 'sum'|_ }} ({{ sum.currency_name }}) ({{ 'left_to_pay_active_bills'|_ }})</small>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right;"> <!-- amount -->
|
||||||
|
{{ formatAmountBySymbol(sum.total_left_to_pay, sum.currency_symbol, sum.currency_decimal_places) }}
|
||||||
|
</td>
|
||||||
|
<td> </td> <!-- paid in period -->
|
||||||
|
<td class="hidden-sm hidden-xs"> </td> <!-- next expected match -->
|
||||||
|
<td class="hidden-sm hidden-xs"> </td><!-- repeats -->
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
{% if '0' != sum.per_period %}
|
{% if '0' != sum.per_period %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="hidden-sm hidden-xs"> </td> <!-- handle -->
|
<td class="hidden-sm hidden-xs"> </td> <!-- handle -->
|
||||||
|
|||||||
@@ -346,9 +346,11 @@
|
|||||||
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
<li><a href="{{ route('transactions.edit', [group.id]) }}"><span
|
<li><a href="{{ route('transactions.edit', [group.id]) }}"><span
|
||||||
class="fa fa-fw fa-pencil"></span> {{ 'edit'|_ }}</a></li>
|
class="fa fa-fw fa-pencil"></span> {{ 'edit'|_ }}</a></li>
|
||||||
{% if transaction.transaction_type_type != 'Reconciliation' and transaction.transaction_type_type != 'Opening balance' and transaction.transaction_type_type != 'Liability credit' %}
|
{% if transaction.transaction_type_type != 'Opening balance' and transaction.transaction_type_type != 'Liability credit' %}
|
||||||
<li><a href="{{ route('transactions.delete', [group.id]) }}"><span
|
<li><a href="{{ route('transactions.delete', [group.id]) }}"><span
|
||||||
class="fa fa-fw fa-trash"></span> {{ 'delete'|_ }}</a></li>
|
class="fa fa-fw fa-trash"></span> {{ 'delete'|_ }}</a></li>
|
||||||
|
{% endif %}
|
||||||
|
{% if transaction.transaction_type_type != 'Reconciliation' and transaction.transaction_type_type != 'Opening balance' and transaction.transaction_type_type != 'Liability credit' %}
|
||||||
<li><a href="#" data-id="{{ group.id }}" class="clone-transaction"><span
|
<li><a href="#" data-id="{{ group.id }}" class="clone-transaction"><span
|
||||||
class="fa fa-copy fa-fw"></span> {{ 'clone'|_ }}</a></li>
|
class="fa fa-copy fa-fw"></span> {{ 'clone'|_ }}</a></li>
|
||||||
<li><a href="#" data-id="{{ group.id }}" class="clone-transaction-and-edit"><span
|
<li><a href="#" data-id="{{ group.id }}" class="clone-transaction-and-edit"><span
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
<td style="text-align: right;">{{ period.total_transactions }}</td>
|
<td style="text-align: right;">{{ period.total_transactions }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% for entry in period.spent %}
|
{% for entry in period.spent %}
|
||||||
{% if entry.amount != 0 %}
|
{% if entry.amount != 0 %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -46,8 +46,12 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: right;" class="piggySaved">
|
<td style="text-align: right;" class="piggySaved">
|
||||||
<span title="Saved so far"
|
<span title="Saved so far" style="text-align:right;">
|
||||||
style="text-align:right;">{{ formatAmountBySymbol(piggy.current_amount,piggy.currency_symbol,piggy.currency_decimal_places) }}</span>
|
{{ formatAmountBySymbol(piggy.current_amount,piggy.currency_symbol,piggy.currency_decimal_places) }}
|
||||||
|
{% if convertToPrimary and piggy.currency_id != primaryCurrency.id and null != piggy.pc_current_amount %}
|
||||||
|
({{ formatAmountBySymbol(piggy.pc_current_amount,primaryCurrency.symbol,primaryCurrency.decimal_places) }})
|
||||||
|
{% endif %}
|
||||||
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden-sm hidden-xs" style="text-align:right;width:40px;">
|
<td class="hidden-sm hidden-xs" style="text-align:right;width:40px;">
|
||||||
{% if piggy.current_amount > 0 %}
|
{% if piggy.current_amount > 0 %}
|
||||||
@@ -86,16 +90,25 @@
|
|||||||
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
||||||
{% if null != piggy.target_amount and 0 != piggy.target_amount %}
|
{% if null != piggy.target_amount and 0 != piggy.target_amount %}
|
||||||
<span title="{{ 'target_amount'|_ }}">{{ formatAmountBySymbol(piggy.target_amount,piggy.currency_symbol,piggy.currency_decimal_places) }}</span>
|
<span title="{{ 'target_amount'|_ }}">{{ formatAmountBySymbol(piggy.target_amount,piggy.currency_symbol,piggy.currency_decimal_places) }}</span>
|
||||||
|
{% if convertToPrimary and piggy.currency_id != primaryCurrency.id and null != piggy.pc_target_amount %}
|
||||||
|
(<span title="{{ 'target_amount'|_ }}">{{ formatAmountBySymbol(piggy.pc_target_amount,primaryCurrency.symbol, primaryCurrency.decimal_places) }}</span>)
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
||||||
{% if piggy.left_to_save > 0 %}
|
{% if piggy.left_to_save > 0 %}
|
||||||
<span title="{{ 'left_to_save'|_ }}">{{ formatAmountBySymbol(piggy.left_to_save,piggy.currency_symbol,piggy.currency_decimal_places) }}</span>
|
<span title="{{ 'left_to_save'|_ }}">{{ formatAmountBySymbol(piggy.left_to_save,piggy.currency_symbol,piggy.currency_decimal_places) }}</span>
|
||||||
|
{% if convertToPrimary and piggy.currency_id != primaryCurrency.id and null != piggy.pc_left_to_save %}
|
||||||
|
(<span title="{{ 'left_to_save'|_ }}">{{ formatAmountBySymbol(piggy.pc_left_to_save, primaryCurrency.symbol,primaryCurrency.decimal_places) }}</span>)
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
||||||
{% if piggy.target_date and piggy.save_per_month %}
|
{% if piggy.target_date and piggy.save_per_month %}
|
||||||
{{ formatAmountBySymbol(piggy.save_per_month, piggy.currency_symbol, piggy.currency_decimal_places) }}
|
{{ formatAmountBySymbol(piggy.save_per_month, piggy.currency_symbol, piggy.currency_decimal_places) }}
|
||||||
|
{% if convertToPrimary and piggy.currency_id != primaryCurrency.id and null != piggy.pc_save_per_month %}
|
||||||
|
({{ formatAmountBySymbol(piggy.pc_save_per_month, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -128,6 +128,12 @@
|
|||||||
<h3 class="box-title">{{ 'quick_link_reports'|_ }}</h3>
|
<h3 class="box-title">{{ 'quick_link_reports'|_ }}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="box-body">
|
<div class="box-body">
|
||||||
|
{% if '' == accountList %}
|
||||||
|
<p class="text-danger">
|
||||||
|
{{ 'quick_link_needs_accounts'|_ }}
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
{% if '' != accountList %}
|
||||||
<p>
|
<p>
|
||||||
{{ 'quick_link_examples'|_ }}
|
{{ 'quick_link_examples'|_ }}
|
||||||
</p>
|
</p>
|
||||||
@@ -172,6 +178,7 @@
|
|||||||
<p>
|
<p>
|
||||||
<em>{{ 'reports_can_bookmark'|_ }}</em>
|
<em>{{ 'reports_can_bookmark'|_ }}</em>
|
||||||
</p>
|
</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,15 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-12 col-sm-12">
|
<div class="col-lg-12 col-md-12 col-sm-12">
|
||||||
<p>
|
<div class="btn-group">
|
||||||
<a href="{{ route('rule-groups.create') }}" id="new_rule_group" class="btn btn-success">{{ 'new_rule_group'|_ }}</a>
|
<a href="{{ route('rule-groups.create') }}" id="new_rule_group" class="btn btn-success">{{ 'new_rule_group'|_ }}</a>
|
||||||
</p>
|
<a href="{{ route('rules.create') }}" class="btn btn-success new_rule">{{ 'new_rule'|_ }}</a>
|
||||||
|
</div>
|
||||||
|
<p></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% if ruleGroups|length == 1 and ruleGroups[0].rules.count() == 0 %}
|
{% if ruleGroups|length == 1 and ruleGroups[0].rules.count() == 0 %}
|
||||||
{% include 'partials.empty' with {objectType: 'default', type: 'rules',route: route('rules.create')} %}
|
{% include 'partials.empty' with {objectType: 'default', type: 'rules',route: route('rules.create')} %}
|
||||||
{# make FF ignore demo for now. #}
|
{# make FF ignore demo for now. #}
|
||||||
@@ -62,6 +65,10 @@
|
|||||||
<p>
|
<p>
|
||||||
<em>{{ ruleGroup.description }}</em>
|
<em>{{ ruleGroup.description }}</em>
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
<a href="{{ route('rules.create', ruleGroup.id) }}"
|
||||||
|
class="btn btn-success new_rule">{{ 'new_rule'|_ }}</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
{% if ruleGroup.rules.count() > 0 %}
|
{% if ruleGroup.rules.count() > 0 %}
|
||||||
<table class="table table-hover table-striped group-rules">
|
<table class="table table-hover table-striped group-rules">
|
||||||
|
|||||||
@@ -64,7 +64,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%;">{{ trans('list.type') }}</td>
|
<td style="width:40%;">{{ trans('list.type') }}</td>
|
||||||
<td>{{ first.transactiontype.type|_ }}</td>
|
<td>{{ first.transaction_type_type|_ }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ trans('list.description') }}</td>
|
<td>{{ trans('list.description') }}</td>
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<div class="box-body no-padding">
|
<div class="box-body no-padding">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover">
|
||||||
<tbody>
|
<tbody>
|
||||||
{% if first.transactiontype.type != 'Withdrawal' or splits == 1 %}
|
{% if first.transaction_type_type != 'Withdrawal' or splits == 1 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%;">
|
<td style="width:40%;">
|
||||||
{{ trans_choice('firefly.source_accounts', accounts['source']|length ) }}
|
{{ trans_choice('firefly.source_accounts', accounts['source']|length ) }}
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if first.transactiontype.type != 'Deposit' or splits == 1 %}
|
{% if first.transaction_type_type != 'Deposit' or splits == 1 %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{ trans_choice('firefly.destination_accounts', accounts['destination']|length ) }}
|
{{ trans_choice('firefly.destination_accounts', accounts['destination']|length ) }}
|
||||||
@@ -159,17 +159,17 @@
|
|||||||
<td style="width:30%;">{{ 'total_amount'|_ }}</td>
|
<td style="width:30%;">{{ 'total_amount'|_ }}</td>
|
||||||
<td>
|
<td>
|
||||||
{% for amount in amounts %}
|
{% for amount in amounts %}
|
||||||
{% if first.transactiontype.type == 'Withdrawal' %}
|
{% if first.transaction_type_type == 'Withdrawal' %}
|
||||||
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
|
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
|
||||||
{% elseif first.transactiontype.type == 'Deposit' %}
|
{% elseif first.transaction_type_type == 'Deposit' %}
|
||||||
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
|
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
|
||||||
{% elseif first.transactiontype.type == 'Transfer' %}
|
{% elseif first.transaction_type_type == 'Transfer' %}
|
||||||
<span class="text-info money-transfer">
|
<span class="text-info money-transfer">
|
||||||
{{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
|
{{ formatAmountBySymbol(amount.amount, amount.symbol, amount.decimal_places, false) }}{% if loop.index0 != amounts|length -1 %}, {% endif %}
|
||||||
</span>
|
</span>
|
||||||
{% elseif first.transactiontype.type == 'Opening balance' %}
|
{% elseif first.transaction_type_type == 'Opening balance' %}
|
||||||
{# Opening balance stored amount is always negative: find out which way the money goes #}
|
{# Opening balance stored amount is always negative: find out which way the money goes #}
|
||||||
{% if groupArray.transactions[0].source_type == 'Initial balance account' %}
|
{% if groupArray.transactions[0].source_account_type == 'Initial balance account' %}
|
||||||
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}
|
{{ formatAmountBySymbol(amount.amount*-1,amount.symbol, amount.decimal_places) }}
|
||||||
{% else %}
|
{% else %}
|
||||||
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}
|
{{ formatAmountBySymbol(amount.amount,amount.symbol, amount.decimal_places) }}
|
||||||
@@ -202,7 +202,7 @@
|
|||||||
{% set boxSize = 4 %}
|
{% set boxSize = 4 %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{% for index,journal in groupArray.transactions %}
|
{% for index,journal in selectedGroup.transactions %}
|
||||||
<div class="col-lg-{{ boxSize }}">
|
<div class="col-lg-{{ boxSize }}">
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="box-header with-border">
|
<div class="box-header with-border">
|
||||||
@@ -289,48 +289,74 @@
|
|||||||
<table class="table">
|
<table class="table">
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
|
<!-- type is: "{{ first.transaction_type_type }}" -->
|
||||||
<!-- type is: "{{ first.transactiontype.type }}" -->
|
{% if 'Cash account' == journal.source_account_type %}
|
||||||
{% if 'Cash account' == journal.source_type %}
|
|
||||||
<span class="text-success">({{ 'cash'|_ }})</span>
|
<span class="text-success">({{ 'cash'|_ }})</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ route('accounts.show', journal.source_id) }}"
|
<a href="{{ route('accounts.show', journal.source_account_id) }}"
|
||||||
title="{{ journal.source_iban|default(journal.source_name) }}">{{ journal.source_name }}</a> →
|
title="{{ journal.source_iban|default(journal.source_account_name) }}">{{ journal.source_account_name }}</a> →
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if first.transactiontype.type == 'Withdrawal' %}
|
{% if first.transaction_type_type == 'Withdrawal' %}
|
||||||
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
|
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places) }}
|
||||||
{% elseif first.transactiontype.type == 'Deposit' %}
|
{% elseif first.transaction_type_type == 'Deposit' %}
|
||||||
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }}
|
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places) }}
|
||||||
{% elseif first.transactiontype.type == 'Transfer' or first.transactiontype.type == 'Opening balance' %}
|
{% elseif first.transaction_type_type == 'Transfer' or first.transaction_type_type == 'Opening balance' %}
|
||||||
<span class="text-info money-transfer">
|
<span class="text-info money-transfer">
|
||||||
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }}
|
{{ formatAmountBySymbol(journal.amount, journal.currency_symbol, journal.currency_decimal_places, false) }}
|
||||||
</span>
|
</span>
|
||||||
{% elseif first.transactiontype.type == 'Liability credit' %}
|
{% elseif first.transaction_type_type == 'Liability credit' %}
|
||||||
<span class="text-info money-transfer">
|
<span class="text-info money-transfer">
|
||||||
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places, false) }}
|
{{ formatAmountBySymbol(journal.amount*-1, journal.currency_symbol, journal.currency_decimal_places, false) }}
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- do primary currency amount -->
|
||||||
|
{% if null != journal.pc_amount and primaryCurrency.id != journal.currency_id %}
|
||||||
|
{% if first.transaction_type_type == 'Withdrawal' %}
|
||||||
|
({{ formatAmountBySymbol(journal.pc_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
||||||
|
{% elseif first.transaction_type_type == 'Deposit' %}
|
||||||
|
({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
||||||
|
{% elseif first.transaction_type_type == 'Transfer' %}
|
||||||
|
<span class="text-info money-transfer">
|
||||||
|
({{ formatAmountBySymbol(journal.pc_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<!-- do foreign amount -->
|
<!-- do foreign amount -->
|
||||||
{% if null != journal.foreign_amount %}
|
{% if null != journal.foreign_amount %}
|
||||||
{% if first.transactiontype.type == 'Withdrawal' %}
|
{% if first.transaction_type_type == 'Withdrawal' %}
|
||||||
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
|
({{ formatAmountBySymbol(journal.foreign_amount*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
|
||||||
{% elseif first.transactiontype.type == 'Deposit' %}
|
{% elseif first.transaction_type_type == 'Deposit' %}
|
||||||
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
|
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places) }})
|
||||||
{% elseif first.transactiontype.type == 'Transfer' %}
|
{% elseif first.transaction_type_type == 'Transfer' %}
|
||||||
<span class="text-info money-transfer">
|
<span class="text-info money-transfer">
|
||||||
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
|
({{ formatAmountBySymbol(journal.foreign_amount, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
|
||||||
</span>
|
</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<!-- do foreign PC amount -->
|
||||||
|
{% if null != journal.pc_foreign_amount %}
|
||||||
|
{% if first.transaction_type_type == 'Withdrawal' %}
|
||||||
|
({{ formatAmountBySymbol(journal.pc_foreign_amount*-1, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
||||||
|
{% elseif first.transaction_type_type == 'Deposit' %}
|
||||||
|
({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places) }})
|
||||||
|
{% elseif first.transaction_type_type == 'Transfer' %}
|
||||||
|
<span class="text-info money-transfer">
|
||||||
|
({{ formatAmountBySymbol(journal.pc_foreign_amount, primaryCurrency.symbol, primaryCurrency.decimal_places, false) }})
|
||||||
|
</span>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
→
|
→
|
||||||
{% if 'Cash account' == journal.destination_type %}
|
{% if 'Cash account' == journal.destination_account_type %}
|
||||||
<span class="text-success">({{ 'cash'|_ }})</span>
|
<span class="text-success">({{ 'cash'|_ }})</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<a href="{{ route('accounts.show', journal.destination_id) }}"
|
<a href="{{ route('accounts.show', journal.destination_account_id) }}"
|
||||||
title="{{ journal.destination_iban|default(journal.destination_name) }}">{{ journal.destination_name }}</a>
|
title="{{ journal.destination_iban|default(journal.destination_account_name) }}">{{ journal.destination_account_name }}</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -346,7 +372,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if null != journal.budget_id and first.transactiontype.type == 'Withdrawal' %}
|
{% if null != journal.budget_id and first.transaction_type_type == 'Withdrawal' %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%;">{{ 'budget'|_ }}</td>
|
<td style="width:40%;">{{ 'budget'|_ }}</td>
|
||||||
<td>
|
<td>
|
||||||
@@ -354,7 +380,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if null != journal.bill_id and first.transactiontype.type == 'Withdrawal' %}
|
{% if null != journal.bill_id and first.transaction_type_type == 'Withdrawal' %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="width:40%;">{{ 'bill'|_ }}</td>
|
<td style="width:40%;">{{ 'bill'|_ }}</td>
|
||||||
<td>
|
<td>
|
||||||
|
|||||||
@@ -6,8 +6,14 @@
|
|||||||
@endcomponent
|
@endcomponent
|
||||||
@endslot
|
@endslot
|
||||||
|
|
||||||
{{-- Body --}}
|
{{-- Body --}}
|
||||||
{{ $slot }}
|
{{ trans('email.greeting') }}
|
||||||
|
|
||||||
|
{{ $slot }}
|
||||||
|
|
||||||
|
{{ trans('email.closing') }}
|
||||||
|
|
||||||
|
{{ trans('email.signature')}}
|
||||||
|
|
||||||
{{-- Subcopy --}}
|
{{-- Subcopy --}}
|
||||||
@isset($subcopy)
|
@isset($subcopy)
|
||||||
|
|||||||
265
routes/api.php
265
routes/api.php
@@ -24,256 +24,6 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* ____ ____ ___ .______ ______ __ __ .___________. _______ _______.
|
|
||||||
* \ \ / / |__ \ | _ \ / __ \ | | | | | || ____| / |
|
|
||||||
* \ \/ / ) | | |_) | | | | | | | | | `---| |----`| |__ | (----`
|
|
||||||
* \ / / / | / | | | | | | | | | | | __| \ \
|
|
||||||
* \ / / /_ | |\ \----.| `--' | | `--' | | | | |____.----) |
|
|
||||||
* \__/ |____| | _| `._____| \______/ \______/ |__| |_______|_______/
|
|
||||||
*/
|
|
||||||
|
|
||||||
// AUTOCOMPLETE ROUTES
|
|
||||||
Route::group(
|
|
||||||
[
|
|
||||||
'namespace' => 'FireflyIII\Api\V2\Controllers\Autocomplete',
|
|
||||||
'prefix' => 'v2/autocomplete',
|
|
||||||
'as' => 'api.v2.autocomplete.',
|
|
||||||
],
|
|
||||||
static function (): void {
|
|
||||||
Route::get('accounts', ['uses' => 'AccountController@accounts', 'as' => 'accounts']);
|
|
||||||
// Route::get('categories', ['uses' => 'CategoryController@categories', 'as' => 'categories']);
|
|
||||||
// Route::get('tags', ['uses' => 'TagController@tags', 'as' => 'tags']);
|
|
||||||
// Route::get('transaction-descriptions', ['uses' => 'TransactionController@transactionDescriptions', 'as' => 'transaction-descriptions']);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// USER GROUP ROUTES
|
|
||||||
Route::group(
|
|
||||||
[
|
|
||||||
'namespace' => 'FireflyIII\Api\V2\Controllers\UserGroup',
|
|
||||||
'prefix' => 'v2/user-groups',
|
|
||||||
'as' => 'api.v2.user-groups.',
|
|
||||||
],
|
|
||||||
static function (): void {
|
|
||||||
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
|
||||||
Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
|
|
||||||
Route::get('{userGroup}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
|
||||||
// Route::put('{userGroup}', ['uses' => 'UpdateController@update', 'as' => 'update']);
|
|
||||||
// Route::post('{userGroup}/use', ['uses' => 'UpdateController@useUserGroup', 'as' => 'use']);
|
|
||||||
// Route::put('{userGroup}/update-membership', ['uses' => 'UpdateController@updateMembership', 'as' => 'updateMembership']);
|
|
||||||
// Route::delete('{userGroup}', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// CHART ROUTES
|
|
||||||
Route::group(
|
|
||||||
[
|
|
||||||
'namespace' => 'FireflyIII\Api\V2\Controllers\Chart',
|
|
||||||
'prefix' => 'v2/chart',
|
|
||||||
'as' => 'api.v2.chart.',
|
|
||||||
],
|
|
||||||
static function (): void {
|
|
||||||
// Route::get('account/dashboard', ['uses' => 'AccountController@dashboard', 'as' => 'account.dashboard']);
|
|
||||||
// Route::get('budget/dashboard', ['uses' => 'BudgetController@dashboard', 'as' => 'budget.dashboard']);
|
|
||||||
// Route::get('category/dashboard', ['uses' => 'CategoryController@dashboard', 'as' => 'category.dashboard']);
|
|
||||||
Route::get('balance/balance', ['uses' => 'BalanceController@balance', 'as' => 'balance.balance']);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// CURRENCY ROUTES
|
|
||||||
Route::group(
|
|
||||||
[
|
|
||||||
'namespace' => 'FireflyIII\Api\V2\Controllers\Model\TransactionCurrency',
|
|
||||||
'prefix' => 'v2/currencies',
|
|
||||||
'as' => 'api.v2.currencies.',
|
|
||||||
],
|
|
||||||
static function (): void {
|
|
||||||
Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
|
||||||
Route::get('{currency_code}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
|
||||||
// Route::post('', ['uses' => 'StoreController@store', 'as' => 'store']);
|
|
||||||
//
|
|
||||||
// Route::put('{userGroup}', ['uses' => 'UpdateController@update', 'as' => 'update']);
|
|
||||||
// Route::post('{userGroup}/use', ['uses' => 'UpdateController@useUserGroup', 'as' => 'use']);
|
|
||||||
// Route::put('{userGroup}/update-membership', ['uses' => 'UpdateController@updateMembership', 'as' => 'updateMembership']);
|
|
||||||
// Route::delete('{userGroup}', ['uses' => 'DestroyController@destroy', 'as' => 'destroy']);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// V2 API route for Summary boxes
|
|
||||||
// BASIC
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Summary',
|
|
||||||
// 'prefix' => 'v2/summary',
|
|
||||||
// 'as' => 'api.v2.summary.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('basic', ['uses' => 'BasicController@basic', 'as' => 'basic']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// // V2 API route for all kinds of Transaction lists.
|
|
||||||
// // A lot of endpoints involve transactions. So any time Firefly III needs to list transactions
|
|
||||||
// // it's coming from these endpoints.
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Transaction\List',
|
|
||||||
// 'prefix' => 'v2',
|
|
||||||
// 'as' => 'api.v2.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // basic list
|
|
||||||
// // Route::get('transactions', ['uses' => 'TransactionController@list', 'as' => 'transactions.list']);
|
|
||||||
//
|
|
||||||
// // list by parent or related object.
|
|
||||||
// // note how the check is done on the user group, not the user itself.
|
|
||||||
// // Route::get('accounts/{userGroupAccount}/transactions', ['uses' => 'AccountController@list', 'as' => 'accounts.transactions']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// V2 API routes for auto complete
|
|
||||||
//
|
|
||||||
// // V2 API route for net worth endpoint(s);
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Summary',
|
|
||||||
// 'prefix' => 'v2/net-worth',
|
|
||||||
// 'as' => 'api.v2.net-worth.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'NetWorthController@get', 'as' => 'index']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // // V2 API route for accounts.
|
|
||||||
// // Route::group(
|
|
||||||
// // [
|
|
||||||
// // 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Account',
|
|
||||||
// // 'prefix' => 'v2/accounts',
|
|
||||||
// // 'as' => 'api.v2.accounts.',
|
|
||||||
// // ],
|
|
||||||
// // static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
|
||||||
// // Route::get('{account}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
|
||||||
// // Route::put('{account}', ['uses' => 'UpdateController@update', 'as' => 'update']);
|
|
||||||
// // }
|
|
||||||
// // );
|
|
||||||
//
|
|
||||||
// // V2 API route for subscriptions.
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Bill',
|
|
||||||
// 'prefix' => 'v2/subscriptions',
|
|
||||||
// 'as' => 'api.v2.subscriptions.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
|
||||||
// // Route::get('{userGroupBill}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
|
||||||
// // Route::get('sum/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
|
|
||||||
// // Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // V2 API route for piggy banks.
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\PiggyBank',
|
|
||||||
// 'prefix' => 'v2/piggy-banks',
|
|
||||||
// 'as' => 'api.v2.piggy-banks.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // V2 API route for transaction currencies
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Currency',
|
|
||||||
// 'prefix' => 'v2/currencies',
|
|
||||||
// 'as' => 'api.v2.currencies.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'IndexController@index', 'as' => 'index']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // V2 API route for transactions
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model\Transaction',
|
|
||||||
// 'prefix' => 'v2/transactions',
|
|
||||||
// 'as' => 'api.v2.transactions.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::post('', ['uses' => 'StoreController@post', 'as' => 'store']);
|
|
||||||
// // Route::get('{userGroupTransaction}', ['uses' => 'ShowController@show', 'as' => 'show']);
|
|
||||||
// // Route::put('{userGroupTransaction}', ['uses' => 'UpdateController@update', 'as' => 'update']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
// // infinite (transactions) list:
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Transaction\List',
|
|
||||||
// 'prefix' => 'v2/infinite/transactions',
|
|
||||||
// 'as' => 'api.v2.infinite.transactions.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'TransactionController@infiniteList', 'as' => 'list']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // V2 API route for budgets and budget limits:
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\Model',
|
|
||||||
// 'prefix' => 'v2/budgets',
|
|
||||||
// 'as' => 'api.v2.budgets',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('', ['uses' => 'Budget\IndexController@index', 'as' => 'index']);
|
|
||||||
// // Route::get('{budget}', ['uses' => 'Budget\ShowController@show', 'as' => 'show']);
|
|
||||||
// // Route::get('{budget}/limits', ['uses' => 'BudgetLimit\IndexController@index', 'as' => 'budget-limits.index']);
|
|
||||||
// // Route::get('sum/budgeted', ['uses' => 'Budget\SumController@budgeted', 'as' => 'sum.budgeted']);
|
|
||||||
// // Route::get('sum/spent', ['uses' => 'Budget\SumController@spent', 'as' => 'sum.spent']);
|
|
||||||
// // Route::get('{budget}/budgeted', ['uses' => 'Budget\ShowController@budgeted', 'as' => 'budget.budgeted']);
|
|
||||||
// // Route::get('{budget}/spent', ['uses' => 'Budget\ShowController@spent', 'as' => 'budget.spent']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// // V2 API route for system
|
|
||||||
// Route::group(
|
|
||||||
// [
|
|
||||||
// 'namespace' => 'FireflyIII\Api\V2\Controllers\System',
|
|
||||||
// 'prefix' => 'v2',
|
|
||||||
// 'as' => 'api.v2.system.',
|
|
||||||
// ],
|
|
||||||
// static function (): void {
|
|
||||||
// // Route::get('preferences/{preference}', ['uses' => 'PreferencesController@get', 'as' => 'preferences.get']);
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
// V2 JSON API ROUTES
|
|
||||||
// JsonApiRoute::server('v2')->prefix('v2')
|
|
||||||
// ->resources(function (ResourceRegistrar $server): void {
|
|
||||||
// // ACCOUNTS
|
|
||||||
// $server->resource('accounts', AccountController::class)
|
|
||||||
// ->relationships(function (Relationships $relations): void {
|
|
||||||
// $relations->hasOne('user')->readOnly();
|
|
||||||
// })
|
|
||||||
// ;
|
|
||||||
//
|
|
||||||
// // USERS
|
|
||||||
// $server->resource('users', JsonApiController::class)->readOnly()->relationships(function (Relationships $relations): void {
|
|
||||||
// $relations->hasMany('accounts')->readOnly();
|
|
||||||
// });
|
|
||||||
// })
|
|
||||||
// ;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ____ ____ __ .______ ______ __ __ .___________. _______ _______.
|
* ____ ____ __ .______ ______ __ __ .___________. _______ _______.
|
||||||
* \ \ / / /_ | | _ \ / __ \ | | | | | || ____| / |
|
* \ \ / / /_ | | _ \ / __ \ | | | | | || ____| / |
|
||||||
@@ -330,6 +80,21 @@ Route::group(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// CHART ROUTES.
|
// CHART ROUTES.
|
||||||
|
|
||||||
|
// chart balance
|
||||||
|
|
||||||
|
// CHART ROUTES
|
||||||
|
Route::group(
|
||||||
|
[
|
||||||
|
'namespace' => 'FireflyIII\Api\V2\Controllers\Chart',
|
||||||
|
'prefix' => 'v1/chart/balance',
|
||||||
|
'as' => 'api.v1.chart.balance',
|
||||||
|
],
|
||||||
|
static function (): void {
|
||||||
|
Route::get('balance', ['uses' => 'BalanceController@balance', 'as' => 'balance.balance']);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Chart accounts
|
// Chart accounts
|
||||||
Route::group(
|
Route::group(
|
||||||
[
|
[
|
||||||
|
|||||||
Reference in New Issue
Block a user