mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-01-22 05:30:10 +00:00
Compare commits
46 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 |
@@ -31,6 +31,7 @@ use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Debug\Timer;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Facades\Steam;
|
||||
use FireflyIII\Support\Http\Api\AccountFilter;
|
||||
use FireflyIII\User;
|
||||
@@ -79,17 +80,20 @@ class AccountController extends Controller
|
||||
*/
|
||||
public function accounts(AutocompleteRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getData();
|
||||
$types = $data['types'];
|
||||
$query = $data['query'];
|
||||
$date = $data['date'] ?? today(config('app.timezone'));
|
||||
$return = [];
|
||||
Timer::start(sprintf('AC accounts "%s"', $query));
|
||||
$result = $this->repository->searchAccount((string) $query, $types, $this->parameters->get('limit'));
|
||||
$data = $request->getData();
|
||||
$types = $data['types'];
|
||||
$query = $data['query'];
|
||||
$date = $data['date'] ?? today(config('app.timezone'));
|
||||
$return = [];
|
||||
$timer = Timer::getInstance();
|
||||
$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
|
||||
$date->endOfDay();
|
||||
|
||||
$allBalances = Steam::accountsBalancesOptimized($result, $date, $this->primaryCurrency, $this->convertToPrimary);
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($result as $account) {
|
||||
$nameWithBalance = $account->name;
|
||||
@@ -98,15 +102,11 @@ class AccountController extends Controller
|
||||
if (in_array($account->accountType->type, $this->balanceTypes, true)) {
|
||||
// this one is correct.
|
||||
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';
|
||||
$useCurrency = $this->convertToPrimary && $currency->id !== $this->primaryCurrency->id ? $this->primaryCurrency : $currency;
|
||||
$amount = $balance[$key] ?? '0';
|
||||
$nameWithBalance = sprintf(
|
||||
'%s (%s)',
|
||||
$account->name,
|
||||
app('amount')->formatAnything($useCurrency, $amount, false)
|
||||
);
|
||||
$nameWithBalance = sprintf('%s (%s)', $account->name, Amount::formatAnything($useCurrency, $amount, false));
|
||||
}
|
||||
|
||||
$return[] = [
|
||||
@@ -138,7 +138,7 @@ class AccountController extends Controller
|
||||
return $posA - $posB;
|
||||
}
|
||||
);
|
||||
Timer::stop(sprintf('AC accounts "%s"', $query));
|
||||
$timer->stop(sprintf('AC accounts "%s"', $query));
|
||||
|
||||
return response()->api($return);
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ class BillEventHandler
|
||||
|
||||
return;
|
||||
}
|
||||
Log::debug(sprintf('Will warning about %d overdue subscription(s).', count($toBeWarned)));
|
||||
Log::debug(sprintf('Will warn about %d overdue subscription(s).', count($toBeWarned)));
|
||||
if (0 === count($toBeWarned)) {
|
||||
Log::debug('No overdue subscriptions to warn about.');
|
||||
|
||||
@@ -84,7 +84,7 @@ class BillEventHandler
|
||||
Log::warning('should hit this ONCE');
|
||||
|
||||
try {
|
||||
Notification::send($user, new SubscriptionsOverdueReminder($overdue));
|
||||
Notification::send($user, new SubscriptionsOverdueReminder($toBeWarned));
|
||||
} catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
if (str_contains($message, 'Bcc')) {
|
||||
|
||||
@@ -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')));
|
||||
$primary = Amount::getPrimaryCurrency();
|
||||
$netWorth = [];
|
||||
Log::debug(sprintf('NetWorth: finalAccountsBalance("%s")', $date->format('Y-m-d H:i:s')));
|
||||
$balances = Steam::finalAccountsBalance($accounts, $date);
|
||||
Log::debug(sprintf('NetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
|
||||
$balances = Steam::accountsBalancesOptimized($accounts, $date, null, $convertToPrimary);
|
||||
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
@@ -143,8 +143,8 @@ class NetWorth implements NetWorthInterface
|
||||
*/
|
||||
$accounts = $this->getAccounts();
|
||||
$return = [];
|
||||
Log::debug(sprintf('SumNetWorth: finalAccountsBalance("%s")', $date->format('Y-m-d H:i:s')));
|
||||
$balances = Steam::finalAccountsBalance($accounts, $date);
|
||||
Log::debug(sprintf('SumNetWorth: accountsBalancesOptimized("%s")', $date->format('Y-m-d H:i:s')));
|
||||
$balances = Steam::accountsBalancesOptimized($accounts, $date);
|
||||
foreach ($accounts as $account) {
|
||||
$currency = $this->accountRepository->getAccountCurrency($account);
|
||||
$balance = $balances[$account->id]['balance'] ?? '0';
|
||||
|
||||
@@ -93,10 +93,10 @@ class IndexController extends Controller
|
||||
$start->subSecond();
|
||||
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
Log::debug(sprintf('inactive start: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('inactive end: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
||||
Log::debug(sprintf('inactive start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('inactive end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
|
||||
|
||||
@@ -170,10 +170,10 @@ class IndexController extends Controller
|
||||
$start->subSecond();
|
||||
|
||||
$ids = $accounts->pluck('id')->toArray();
|
||||
Log::debug(sprintf('index start: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('index end: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
||||
Log::debug(sprintf('index start: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('index end: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$activities = Steam::getLastActivities($ids);
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ use Illuminate\Routing\Redirector;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\View\View;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class ShowController
|
||||
@@ -81,7 +82,9 @@ class ShowController extends Controller
|
||||
* */
|
||||
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));
|
||||
|
||||
if (!$this->isEditableAccount($account)) {
|
||||
@@ -115,19 +118,27 @@ class ShowController extends Controller
|
||||
$chartUrl = route('chart.account.period', [$account->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||
$firstTransaction = $this->repository->oldestJournalDate($account) ?? $start;
|
||||
|
||||
// go back max 3 years.
|
||||
$threeYearsAgo = clone $start;
|
||||
$threeYearsAgo->startOfYear()->subYears(3);
|
||||
if ($firstTransaction->lt($threeYearsAgo)) {
|
||||
$firstTransaction = clone $threeYearsAgo;
|
||||
}
|
||||
|
||||
Log::debug('Start period overview');
|
||||
Timer::start('period-overview');
|
||||
$timer = Timer::getInstance();
|
||||
$timer->start('period-overview');
|
||||
$periods = $this->getAccountPeriodOverview($account, $firstTransaction, $end);
|
||||
|
||||
Log::debug('End period overview');
|
||||
Timer::stop('period-overview');
|
||||
$timer->stop('period-overview');
|
||||
|
||||
// if layout = v2, overrule the page title.
|
||||
if ('v1' !== config('view.layout')) {
|
||||
$subTitle = (string) trans('firefly.all_journals_for_account', ['name' => $account->name]);
|
||||
}
|
||||
Log::debug('Collect transactions');
|
||||
Timer::start('collection');
|
||||
$timer->start('collection');
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
@@ -146,7 +157,7 @@ class ShowController extends Controller
|
||||
|
||||
|
||||
Log::debug('End collect transactions');
|
||||
Timer::stop('collection');
|
||||
$timer->stop('collection');
|
||||
|
||||
// enrich data in arrays.
|
||||
|
||||
|
||||
@@ -114,11 +114,11 @@ class AccountController extends Controller
|
||||
$accountNames = $this->extractNames($accounts);
|
||||
|
||||
// grab all balances
|
||||
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('expenseAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::finalAccountsBalance($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::finalAccountsBalance($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
|
||||
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('expenseAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
Log::debug('Done collecting balances');
|
||||
// loop the accounts, then check for balance and currency info.
|
||||
foreach ($accounts as $account) {
|
||||
// Log::debug(sprintf('[a] Now in account #%d ("%s")', $account->id, $account->name));
|
||||
@@ -654,10 +654,10 @@ class AccountController extends Controller
|
||||
$accountNames = $this->extractNames($accounts);
|
||||
|
||||
// grab all balances
|
||||
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('revAccounts: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::finalAccountsBalance($accounts, $start);
|
||||
$endBalances = Steam::finalAccountsBalance($accounts, $end);
|
||||
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $start->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('revAccounts: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startBalances = Steam::accountsBalancesOptimized($accounts, $start, $this->primaryCurrency, $this->convertToPrimary);
|
||||
$endBalances = Steam::accountsBalancesOptimized($accounts, $end, $this->primaryCurrency, $this->convertToPrimary);
|
||||
|
||||
|
||||
// loop the accounts, then check for balance and currency info.
|
||||
|
||||
@@ -188,16 +188,7 @@ class ReportController extends Controller
|
||||
$start->endOfDay(); // end of day so the final balance is at the end of that day.
|
||||
$end->endOfDay();
|
||||
|
||||
app('view')->share(
|
||||
'subTitle',
|
||||
trans(
|
||||
'firefly.report_default',
|
||||
[
|
||||
'start' => $start->isoFormat($this->monthAndDayFormat),
|
||||
'end' => $end->isoFormat($this->monthAndDayFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
app('view')->share('subTitle', trans('firefly.report_default', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]));
|
||||
|
||||
$generator = ReportGeneratorFactory::reportGenerator('Standard', $start, $end);
|
||||
$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.
|
||||
$end->endOfDay();
|
||||
|
||||
app('view')->share(
|
||||
'subTitle',
|
||||
trans(
|
||||
'firefly.report_double',
|
||||
[
|
||||
'start' => $start->isoFormat($this->monthAndDayFormat),
|
||||
'end' => $end->isoFormat($this->monthAndDayFormat),
|
||||
]
|
||||
)
|
||||
);
|
||||
app('view')->share('subTitle', trans('firefly.report_double', ['start' => $start->isoFormat($this->monthAndDayFormat), 'end' => $end->isoFormat($this->monthAndDayFormat)]));
|
||||
|
||||
$generator = ReportGeneratorFactory::reportGenerator('Account', $start, $end);
|
||||
$generator->setAccounts($accounts);
|
||||
|
||||
@@ -92,13 +92,12 @@ class SubscriptionsOverdueReminder extends Notification
|
||||
*/
|
||||
public function toSlack(User $notifiable): SlackMessage
|
||||
{
|
||||
$bill = $this->bill;
|
||||
$url = route('bills.show', [$bill->id]);
|
||||
$url = route('bills.index');
|
||||
|
||||
return new SlackMessage()
|
||||
->warning()
|
||||
->attachment(static function ($attachment) use ($bill, $url): void {
|
||||
$attachment->title((string)trans('firefly.visit_bill', ['name' => $bill->name]), $url);
|
||||
->attachment(static function ($attachment) use ($url): void {
|
||||
$attachment->title((string)trans('firefly.visit_bills'), $url);
|
||||
})
|
||||
->content($this->getSubject())
|
||||
;
|
||||
|
||||
@@ -566,7 +566,7 @@ class AccountRepository implements AccountRepositoryInterface, UserGroupInterfac
|
||||
'transaction_types.type',
|
||||
'transaction_journals.transaction_currency_id',
|
||||
'transactions.amount',
|
||||
'transactions.native_amount',
|
||||
'transactions.native_amount as pc_amount',
|
||||
'transactions.foreign_amount',
|
||||
])
|
||||
->toArray()
|
||||
|
||||
@@ -50,10 +50,10 @@ class AccountTasker implements AccountTaskerInterface, UserGroupInterface
|
||||
$yesterday = clone $start;
|
||||
$yesterday->subDay()->endOfDay(); // exactly up until $start but NOT including.
|
||||
$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: finalAccountsBalance("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startSet = Steam::finalAccountsBalance($accounts, $yesterday);
|
||||
$endSet = Steam::finalAccountsBalance($accounts, $end);
|
||||
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $yesterday->format('Y-m-d H:i:s')));
|
||||
Log::debug(sprintf('getAccountReport: accountsBalancesOptimized("%s")', $end->format('Y-m-d H:i:s')));
|
||||
$startSet = Steam::accountsBalancesOptimized($accounts, $yesterday);
|
||||
$endSet = Steam::accountsBalancesOptimized($accounts, $end);
|
||||
Log::debug('Start of accountreport');
|
||||
|
||||
/** @var AccountRepositoryInterface $repository */
|
||||
|
||||
@@ -28,19 +28,34 @@ use Illuminate\Support\Facades\Log;
|
||||
|
||||
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);
|
||||
$diff = $end - $start;
|
||||
unset(self::$times[$title]);
|
||||
unset($this->times[$title]);
|
||||
Log::debug(sprintf('Timer "%s" took %f seconds', $title, $diff));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,8 @@ trait PeriodOverview
|
||||
protected function getAccountPeriodOverview(Account $account, Carbon $start, Carbon $end): array
|
||||
{
|
||||
Log::debug('Now in getAccountPeriodOverview()');
|
||||
Timer::start('account-period-total');
|
||||
$timer = Timer::getInstance();
|
||||
$timer->start('account-period-total');
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$range = Navigation::getViewRange(true);
|
||||
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
|
||||
@@ -92,23 +93,24 @@ trait PeriodOverview
|
||||
$cache->addProperty('account-show-period-entries');
|
||||
$cache->addProperty($account->id);
|
||||
if ($cache->has()) {
|
||||
// return $cache->get();
|
||||
Log::debug('Return CACHED in getAccountPeriodOverview()');
|
||||
|
||||
return $cache->get();
|
||||
}
|
||||
|
||||
/** @var array $dates */
|
||||
$dates = Navigation::blockPeriods($start, $end, $range);
|
||||
$entries = [];
|
||||
$spent = [];
|
||||
$earned = [];
|
||||
$transferredAway = [];
|
||||
$transferredIn = [];
|
||||
|
||||
// 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);
|
||||
$timer->stop('account-period-collect');
|
||||
// loop dates
|
||||
Log::debug(sprintf('Count of loops: %d', count($dates)));
|
||||
$loops = 0;
|
||||
// stop after 10 loops for memory reasons.
|
||||
$timer->start('account-period-loop');
|
||||
foreach ($dates as $currentDate) {
|
||||
$title = Navigation::periodShow($currentDate['start'], $currentDate['period']);
|
||||
[$transactions, $spent] = $this->filterTransactionsByType(TransactionTypeEnum::WITHDRAWAL, $transactions, $currentDate['start'], $currentDate['end']);
|
||||
@@ -127,8 +129,9 @@ trait PeriodOverview
|
||||
];
|
||||
++$loops;
|
||||
}
|
||||
$timer->stop('account-period-loop');
|
||||
$cache->store($entries);
|
||||
Timer::stop('account-period-total');
|
||||
$timer->stop('account-period-total');
|
||||
Log::debug('End of getAccountPeriodOverview()');
|
||||
|
||||
return $entries;
|
||||
@@ -168,7 +171,7 @@ trait PeriodOverview
|
||||
* @var array $item
|
||||
*/
|
||||
foreach ($transactions as $index => $item) {
|
||||
$date = Carbon::parse($item['date']);
|
||||
$date = Carbon::parse($item['date']);
|
||||
if ($date >= $start && $date <= $end) {
|
||||
if ('away' === $direction && -1 === bccomp((string)$item['amount'], '0')) {
|
||||
$result[] = $item;
|
||||
@@ -180,8 +183,8 @@ trait PeriodOverview
|
||||
|
||||
continue;
|
||||
}
|
||||
$filtered[] = $item;
|
||||
}
|
||||
$filtered[] = $item;
|
||||
}
|
||||
|
||||
return [$filtered, $result];
|
||||
|
||||
@@ -345,7 +345,7 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
|
||||
private function getLastPaidDate(array $paidData): ?Carbon
|
||||
{
|
||||
Log::debug('getLastPaidDate()');
|
||||
// Log::debug('getLastPaidDate()');
|
||||
$return = null;
|
||||
foreach ($paidData as $entry) {
|
||||
if (null !== $return) {
|
||||
@@ -354,15 +354,15 @@ class SubscriptionEnrichment implements EnrichmentInterface
|
||||
if ($current->gt($return)) {
|
||||
$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) {
|
||||
/** @var Carbon $return */
|
||||
$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;
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Support\Facades\Amount;
|
||||
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
|
||||
use FireflyIII\Support\Singleton\PreferencesSingleton;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
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
|
||||
{
|
||||
$result = [];
|
||||
Log::debug(sprintf('accountsBalancesOptimized: Called for %d account(s) with date/time "%s"', $accounts->count(), $date->toIso8601String()));
|
||||
$result = [];
|
||||
$convertToPrimary ??= Amount::convertToPrimary();
|
||||
$primary ??= Amount::getPrimaryCurrency();
|
||||
$currencies = $this->getCurrencies($accounts);
|
||||
$currencies = $this->getCurrencies($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_currencies', 'transaction_currencies.id', '=', 'transactions.transaction_currency_id')
|
||||
->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 */
|
||||
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.
|
||||
$return = [
|
||||
'pc_balance' => '0',
|
||||
'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);
|
||||
// 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) {
|
||||
unset($return['pc_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 ($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']));
|
||||
}
|
||||
|
||||
@@ -377,7 +388,7 @@ class Steam
|
||||
$return['balance'] = bcadd($return['balance'], $virtualBalance);
|
||||
// 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;
|
||||
// Log::debug('Final balance is', $final);
|
||||
}
|
||||
@@ -507,30 +518,27 @@ class Steam
|
||||
{
|
||||
$total = '0';
|
||||
$converter = new ExchangeRateConverter();
|
||||
$singleton = PreferencesSingleton::getInstance();
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
$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);
|
||||
if (null === $preference) {
|
||||
$singleton->setPreference($key, $currency);
|
||||
}
|
||||
$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;
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
47
changelog.md
47
changelog.md
@@ -3,6 +3,53 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
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
|
||||
|
||||
### Added
|
||||
|
||||
79
composer.lock
generated
79
composer.lock
generated
@@ -586,33 +586,32 @@
|
||||
},
|
||||
{
|
||||
"name": "doctrine/inflector",
|
||||
"version": "2.0.10",
|
||||
"version": "2.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/doctrine/inflector.git",
|
||||
"reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc"
|
||||
"reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc",
|
||||
"reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc",
|
||||
"url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b",
|
||||
"reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2 || ^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"doctrine/coding-standard": "^11.0",
|
||||
"phpstan/phpstan": "^1.8",
|
||||
"phpstan/phpstan-phpunit": "^1.1",
|
||||
"phpstan/phpstan-strict-rules": "^1.3",
|
||||
"phpunit/phpunit": "^8.5 || ^9.5",
|
||||
"vimeo/psalm": "^4.25 || ^5.4"
|
||||
"doctrine/coding-standard": "^12.0 || ^13.0",
|
||||
"phpstan/phpstan": "^1.12 || ^2.0",
|
||||
"phpstan/phpstan-phpunit": "^1.4 || ^2.0",
|
||||
"phpstan/phpstan-strict-rules": "^1.6 || ^2.0",
|
||||
"phpunit/phpunit": "^8.5 || ^12.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Doctrine\\Inflector\\": "lib/Doctrine/Inflector"
|
||||
"Doctrine\\Inflector\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -657,7 +656,7 @@
|
||||
],
|
||||
"support": {
|
||||
"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": [
|
||||
{
|
||||
@@ -673,7 +672,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-02-18T20:23:39+00:00"
|
||||
"time": "2025-08-10T19:31:58+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/lexer",
|
||||
@@ -11618,16 +11617,16 @@
|
||||
},
|
||||
{
|
||||
"name": "phpunit/phpunit",
|
||||
"version": "12.3.0",
|
||||
"version": "12.3.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||
"reference": "264da860d6fe0d00582355a6ecbbf7ae57b44895"
|
||||
"reference": "ac6952c92e8a66ee5698cf81f421120ff64c8d0f"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/264da860d6fe0d00582355a6ecbbf7ae57b44895",
|
||||
"reference": "264da860d6fe0d00582355a6ecbbf7ae57b44895",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/ac6952c92e8a66ee5698cf81f421120ff64c8d0f",
|
||||
"reference": "ac6952c92e8a66ee5698cf81f421120ff64c8d0f",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11637,7 +11636,7 @@
|
||||
"ext-mbstring": "*",
|
||||
"ext-xml": "*",
|
||||
"ext-xmlwriter": "*",
|
||||
"myclabs/deep-copy": "^1.13.3",
|
||||
"myclabs/deep-copy": "^1.13.4",
|
||||
"phar-io/manifest": "^2.0.4",
|
||||
"phar-io/version": "^3.2.1",
|
||||
"php": ">=8.3",
|
||||
@@ -11647,13 +11646,13 @@
|
||||
"phpunit/php-text-template": "^5.0.0",
|
||||
"phpunit/php-timer": "^8.0.0",
|
||||
"sebastian/cli-parser": "^4.0.0",
|
||||
"sebastian/comparator": "^7.1.0",
|
||||
"sebastian/comparator": "^7.1.1",
|
||||
"sebastian/diff": "^7.0.0",
|
||||
"sebastian/environment": "^8.0.2",
|
||||
"sebastian/exporter": "^7.0.0",
|
||||
"sebastian/global-state": "^8.0.0",
|
||||
"sebastian/object-enumerator": "^7.0.0",
|
||||
"sebastian/type": "^6.0.2",
|
||||
"sebastian/type": "^6.0.3",
|
||||
"sebastian/version": "^6.0.0",
|
||||
"staabm/side-effects-detector": "^1.0.5"
|
||||
},
|
||||
@@ -11695,7 +11694,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||
"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": [
|
||||
{
|
||||
@@ -11719,7 +11718,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-08-01T05:14:47+00:00"
|
||||
"time": "2025-08-10T08:36:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "rector/rector",
|
||||
@@ -11840,16 +11839,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/comparator",
|
||||
"version": "7.1.0",
|
||||
"version": "7.1.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/comparator.git",
|
||||
"reference": "03d905327dccc0851c9a08d6a979dfc683826b6f"
|
||||
"reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f",
|
||||
"reference": "03d905327dccc0851c9a08d6a979dfc683826b6f",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1a7c2bce03a13a457ed3c975dfd331b3b4b133aa",
|
||||
"reference": "1a7c2bce03a13a457ed3c975dfd331b3b4b133aa",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -11908,7 +11907,7 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/comparator/issues",
|
||||
"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": [
|
||||
{
|
||||
@@ -11928,7 +11927,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-06-17T07:41:58+00:00"
|
||||
"time": "2025-08-10T08:50:08+00:00"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/complexity",
|
||||
@@ -12509,16 +12508,16 @@
|
||||
},
|
||||
{
|
||||
"name": "sebastian/type",
|
||||
"version": "6.0.2",
|
||||
"version": "6.0.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/sebastianbergmann/type.git",
|
||||
"reference": "1d7cd6e514384c36d7a390347f57c385d4be6069"
|
||||
"reference": "e549163b9760b8f71f191651d22acf32d56d6d4d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069",
|
||||
"reference": "1d7cd6e514384c36d7a390347f57c385d4be6069",
|
||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/e549163b9760b8f71f191651d22acf32d56d6d4d",
|
||||
"reference": "e549163b9760b8f71f191651d22acf32d56d6d4d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -12554,15 +12553,27 @@
|
||||
"support": {
|
||||
"issues": "https://github.com/sebastianbergmann/type/issues",
|
||||
"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": [
|
||||
{
|
||||
"url": "https://github.com/sebastianbergmann",
|
||||
"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",
|
||||
|
||||
@@ -78,8 +78,8 @@ return [
|
||||
'running_balance_column' => env('USE_RUNNING_BALANCE', false),
|
||||
// see cer.php for exchange rates feature flag.
|
||||
],
|
||||
'version' => 'develop/2025-08-09',
|
||||
'build_time' => 1754721409,
|
||||
'version' => 'develop/2025-08-11',
|
||||
'build_time' => 1754883282,
|
||||
'api_version' => '2.1.0', // field is no longer used.
|
||||
'db_version' => 26,
|
||||
|
||||
|
||||
22
package-lock.json
generated
22
package-lock.json
generated
@@ -4326,9 +4326,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/browserslist": {
|
||||
"version": "4.25.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz",
|
||||
"integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==",
|
||||
"version": "4.25.2",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz",
|
||||
"integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -4346,8 +4346,8 @@
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"caniuse-lite": "^1.0.30001726",
|
||||
"electron-to-chromium": "^1.5.173",
|
||||
"caniuse-lite": "^1.0.30001733",
|
||||
"electron-to-chromium": "^1.5.199",
|
||||
"node-releases": "^2.0.19",
|
||||
"update-browserslist-db": "^1.1.3"
|
||||
},
|
||||
@@ -4486,9 +4486,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001733",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001733.tgz",
|
||||
"integrity": "sha512-e4QKw/O2Kavj2VQTKZWrwzkt3IxOmIlU6ajRb6LP64LHpBo1J67k2Hi4Vu/TgJWsNtynurfS0uK3MaUTCPfu5Q==",
|
||||
"version": "1.0.30001734",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001734.tgz",
|
||||
"integrity": "sha512-uhE1Ye5vgqju6OI71HTQqcBCZrvHugk0MjLak7Q+HfoBgoq5Bi+5YnwjP4fjDgrtYr/l8MVRBvzz9dPD4KyK0A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -7052,9 +7052,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "25.3.2",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.2.tgz",
|
||||
"integrity": "sha512-JSnbZDxRVbphc5jiptxr3o2zocy5dEqpVm9qCGdJwRNO+9saUJS0/u4LnM/13C23fUEWxAylPqKU/NpMV/IjqA==",
|
||||
"version": "25.3.4",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-25.3.4.tgz",
|
||||
"integrity": "sha512-AHklEYFLiRRxW1Cb6zE9lfnEtYvsydRC8nRS3RSKGX3zCqZ8nLZwMaUsrb80YuccPNv2RNokDL8LkTNnp+6mDw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"administrations_index_menu": "Administraciones financieras",
|
||||
"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.",
|
||||
"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}\"",
|
||||
"table": "Mesa",
|
||||
"welcome_back": "\u00bfQu\u00e9 est\u00e1 pasando?",
|
||||
@@ -154,7 +154,7 @@
|
||||
"url": "URL",
|
||||
"active": "Activo",
|
||||
"interest_date": "Fecha de inter\u00e9s",
|
||||
"administration_currency": "Primary currency",
|
||||
"administration_currency": "Moneda Principal",
|
||||
"title": "T\u00edtulo",
|
||||
"date": "Fecha",
|
||||
"book_date": "Fecha de registro",
|
||||
@@ -174,7 +174,7 @@
|
||||
"list": {
|
||||
"title": "T\u00edtulo",
|
||||
"active": "\u00bfEst\u00e1 Activo?",
|
||||
"primary_currency": "Primary currency",
|
||||
"primary_currency": "Moneda principal",
|
||||
"trigger": "Disparador",
|
||||
"response": "Respuesta",
|
||||
"delivery": "Entrega",
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"administrations_page_title": "Administrations financi\u00e8res",
|
||||
"administrations_index_menu": "Administrations financi\u00e8res",
|
||||
"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.",
|
||||
"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.",
|
||||
"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": "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}\"",
|
||||
"table": "Tableau",
|
||||
"welcome_back": "Quoi de neuf ?",
|
||||
@@ -154,7 +154,7 @@
|
||||
"url": "Liens",
|
||||
"active": "Actif",
|
||||
"interest_date": "Date de valeur (int\u00e9r\u00eats)",
|
||||
"administration_currency": "Primary currency",
|
||||
"administration_currency": "Devise principale",
|
||||
"title": "Titre",
|
||||
"date": "Date",
|
||||
"book_date": "Date d'enregistrement",
|
||||
@@ -174,7 +174,7 @@
|
||||
"list": {
|
||||
"title": "Titre",
|
||||
"active": "Actif ?",
|
||||
"primary_currency": "Primary currency",
|
||||
"primary_currency": "Devise principale",
|
||||
"trigger": "D\u00e9clencheur",
|
||||
"response": "R\u00e9ponse",
|
||||
"delivery": "Distribution",
|
||||
|
||||
@@ -141,9 +141,11 @@ return [
|
||||
// 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' => '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' => 'Perhaps you have simply not linked a transaction to these subscription(s). In that case, please do so. You will NOT get another warning about these overdue bill(s).',
|
||||
'subscriptions_overdue_outro' => 'If you believe this message is wrong, please contact the Firefly III developer.',
|
||||
'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_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',
|
||||
|
||||
@@ -1875,6 +1875,7 @@ return [
|
||||
'subscr_expected_x_times' => 'Expect to pay {{amount}} {{times}} times this period',
|
||||
'not_or_not_yet' => 'Not (yet)',
|
||||
'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.',
|
||||
'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',
|
||||
@@ -2332,6 +2333,7 @@ return [
|
||||
|
||||
|
||||
// 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_audit' => 'Transaction history overview between :start and :end',
|
||||
'report_category' => 'Category report between :start and :end',
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
@component('mail::message')
|
||||
{{ trans('email.subscriptions_overdue_warning_intro', ['count' => $count]) }}
|
||||
|
||||
@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)
|
||||
@@ -8,10 +12,13 @@
|
||||
@endforeach
|
||||
@endforeach
|
||||
|
||||
{{ trans('email.subscriptions_overdue_please_action') }}
|
||||
@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
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
<td style="text-align: right;">{{ period.total_transactions }}</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
{% for entry in period.spent %}
|
||||
{% if entry.amount != 0 %}
|
||||
<tr>
|
||||
|
||||
@@ -46,8 +46,12 @@
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: right;" class="piggySaved">
|
||||
<span title="Saved so far"
|
||||
style="text-align:right;">{{ formatAmountBySymbol(piggy.current_amount,piggy.currency_symbol,piggy.currency_decimal_places) }}</span>
|
||||
<span title="Saved so far" style="text-align:right;">
|
||||
{{ 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 class="hidden-sm hidden-xs" style="text-align:right;width:40px;">
|
||||
{% if piggy.current_amount > 0 %}
|
||||
@@ -86,16 +90,25 @@
|
||||
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
||||
{% 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>
|
||||
{% 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 %}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
||||
{% if piggy.left_to_save > 0 %}
|
||||
<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 %}
|
||||
</td>
|
||||
<td class="hidden-sm hidden-xs" style="text-align:right;">
|
||||
{% if piggy.target_date and piggy.save_per_month %}
|
||||
{{ 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 %}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -128,6 +128,12 @@
|
||||
<h3 class="box-title">{{ 'quick_link_reports'|_ }}</h3>
|
||||
</div>
|
||||
<div class="box-body">
|
||||
{% if '' == accountList %}
|
||||
<p class="text-danger">
|
||||
{{ 'quick_link_needs_accounts'|_ }}
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if '' != accountList %}
|
||||
<p>
|
||||
{{ 'quick_link_examples'|_ }}
|
||||
</p>
|
||||
@@ -172,6 +178,7 @@
|
||||
<p>
|
||||
<em>{{ 'reports_can_bookmark'|_ }}</em>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user