Update files for new release

This commit is contained in:
James Cole 2023-09-03 17:38:54 +02:00
parent 9ea3c4224e
commit ed1fdf9382
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
11 changed files with 566 additions and 222 deletions

View File

@ -24,7 +24,9 @@ declare(strict_types=1);
namespace FireflyIII\Api\V2\Controllers\Model\Bill;
use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
use FireflyIII\Transformers\V2\AccountTransformer;
use FireflyIII\Transformers\V2\BillTransformer;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
@ -71,4 +73,17 @@ class ShowController extends Controller
->json($this->jsonApiList('subscriptions', $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE);
}
/**
* TODO this endpoint is not documented
*/
public function show(Request $request, Bill $bill): JsonResponse
{
$transformer = new BillTransformer();
$transformer->setParameters($this->parameters);
return response()
->api($this->jsonApiObject('subscriptions', $bill, $transformer))
->header('Content-Type', self::CONTENT_TYPE);
}
}

View File

@ -138,7 +138,7 @@ class IndexController extends Controller
// get budgeted for default currency:
if (0 === count($availableBudgets)) {
$budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency, );
$budgeted = $this->blRepository->budgeted($start, $end, $defaultCurrency,);
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency);
$spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0';
unset($spentArr);
@ -197,7 +197,7 @@ class IndexController extends Controller
$array['spent'] = $spentArr[$entry->transaction_currency_id]['sum'] ?? '0';
// budgeted in period:
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency, );
$budgeted = $this->blRepository->budgeted($entry->start_date, $entry->end_date, $entry->transactionCurrency,);
$array['budgeted'] = $budgeted;
$availableBudgets[] = $array;
unset($spentArr);
@ -227,6 +227,7 @@ class IndexController extends Controller
Log::debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name));
$array = $current->toArray();
$array['spent'] = [];
$array['spent_total'] = [];
$array['budgeted'] = [];
$array['attachments'] = $this->repository->getAttachments($current);
$array['auto_budget'] = $this->repository->getAutoBudget($current);
@ -235,9 +236,10 @@ class IndexController extends Controller
foreach ($budgetLimits as $limit) {
Log::debug(sprintf('Working on budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$amount = app('steam')->bcround($limit->amount, $currency->decimal_places);
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => app('steam')->bcround($limit->amount, $currency->decimal_places),
'amount' => $amount,
'start_date' => $limit->start_date->isoFormat($this->monthAndDayFormat),
'end_date' => $limit->end_date->isoFormat($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),
@ -246,6 +248,7 @@ class IndexController extends Controller
'currency_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places,
];
Log::debug(sprintf('The amount budgeted for budget limit #%d is %s %s', $limit->id, $currency->code, $amount));
}
/** @var TransactionCurrency $currency */

View File

@ -28,7 +28,10 @@ use Exception;
use FireflyConfig;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
@ -37,6 +40,8 @@ use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
use Monolog\Handler\RotatingFileHandler;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/**
* Class DebugController
@ -117,86 +122,8 @@ class DebugController extends Controller
*/
public function index(Request $request)
{
// basic scope information:
$now = now(config('app.timezone'))->format('Y-m-d H:i:s e');
$buildNr = '(unknown)';
$buildDate = '(unknown)';
$baseBuildNr = '(unknown)';
$baseBuildDate = '(unknown)';
$expectedDBversion = config('firefly.db_version');
$foundDBversion = FireflyConfig::get('db_version', 1)->data;
try {
if (file_exists('/var/www/counter-main.txt')) {
$buildNr = trim(file_get_contents('/var/www/counter-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check counter.');
Log::warning($e->getMessage());
}
try {
if (file_exists('/var/www/build-date-main.txt')) {
$buildDate = trim(file_get_contents('/var/www/build-date-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check date.');
Log::warning($e->getMessage());
}
if ('' !== (string)env('BASE_IMAGE_BUILD')) {
$baseBuildNr = env('BASE_IMAGE_BUILD');
}
if ('' !== (string)env('BASE_IMAGE_DATE')) {
$baseBuildDate = env('BASE_IMAGE_DATE');
}
$phpVersion = PHP_VERSION;
$phpOs = PHP_OS;
// system information
$tz = env('TZ');
$appEnv = config('app.env');
$appDebug = var_export(config('app.debug'), true);
$cacheDriver = config('cache.default');
$logChannel = config('logging.default');
$appLogLevel = config('logging.level');
$maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = app('steam')->phpBytes(ini_get('post_max_size'));
$uploadSize = min($maxFileSize, $maxPostSize);
$displayErrors = ini_get('display_errors');
$errorReporting = $this->errorReporting((int)ini_get('error_reporting'));
$interface = PHP_SAPI;
$defaultLanguage = (string)config('firefly.default_language');
$defaultLocale = (string)config('firefly.default_locale');
$bcscale = bcscale();
$drivers = implode(', ', DB::availableDrivers());
$currentDriver = DB::getDriverName();
$trustedProxies = config('firefly.trusted_proxies');
// user info
$loginProvider = config('auth.providers.users.driver');
$userGuard = config('auth.defaults.guard');
$remoteHeader = $userGuard === 'remote_user_guard' ? config('auth.guard_header') : 'N/A';
$remoteMailHeader = $userGuard === 'remote_user_guard' ? config('auth.guard_email') : 'N/A';
$userLanguage = app('steam')->getLanguage();
$userLocale = app('steam')->getLocale();
$userAgent = $request->header('user-agent');
$stateful = join(', ', config('sanctum.stateful'));
// expected + found DB version:
// some new vars.
$isDocker = env('IS_DOCKER', false);
// set languages, see what happens:
$original = setlocale(LC_ALL, 0);
$localeAttempts = [];
$parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) {
$code = trim($code);
Log::debug(sprintf('Trying to set %s', $code));
$localeAttempts[$code] = var_export(setlocale(LC_ALL, $code), true);
}
setlocale(LC_ALL, $original);
$table = $this->generateTable();
$now = now(config('app.timezone'))->format('Y-m-d H:i:s');
// get latest log file:
$logger = Log::driver();
@ -213,50 +140,190 @@ class DebugController extends Controller
}
if ('' !== $logContent) {
// last few lines
$logContent = 'Truncated from this point <----|' . substr($logContent, -8192);
$logContent = 'Truncated from this point <----|' . substr($logContent, -16384);
}
return view(
'debug',
compact(
'phpVersion',
'localeAttempts',
'expectedDBversion',
'foundDBversion',
'appEnv',
'appDebug',
'logChannel',
'stateful',
'tz',
'uploadSize',
'appLogLevel',
'remoteHeader',
'remoteMailHeader',
'now',
'drivers',
'currentDriver',
'userGuard',
'loginProvider',
'buildNr',
'buildDate',
'baseBuildNr',
'baseBuildDate',
'bcscale',
'userAgent',
'displayErrors',
'errorReporting',
'phpOs',
'interface',
'logContent',
'cacheDriver',
'trustedProxies',
'userLanguage',
'userLocale',
'defaultLanguage',
'defaultLocale',
'isDocker'
)
);
return view('debug', compact('table', 'now', 'logContent'));
}
/**
* @return string
*/
private function generateTable(): string
{
// system information:
$system = $this->getSystemInformation();
$docker = $this->getBuildInfo();
$app = $this->getAppInfo();
$user = $this->getuserInfo();
return (string)view('partials.debug-table', compact('system', 'docker', 'app', 'user'));
}
/**
* @return array
*/
private function getSystemInformation(): array
{
$maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize'));
$maxPostSize = app('steam')->phpBytes(ini_get('post_max_size'));
$drivers = DB::availableDrivers();
$currentDriver = DB::getDriverName();
return [
'db_version' => app('fireflyconfig')->get('db_version', 1)->data,
'php_version' => PHP_VERSION,
'php_os' => PHP_OS,
'interface' => PHP_SAPI,
'bcscale' => bcscale(),
'display_errors' => ini_get('display_errors'),
'error_reporting' => $this->errorReporting((int)ini_get('error_reporting')),
'upload_size' => min($maxFileSize, $maxPostSize),
'all_drivers' => $drivers,
'current_driver' => $currentDriver,
];
}
/**
* @return array
*/
private function getBuildInfo(): array
{
$return = [
'is_docker' => env('IS_DOCKER', false),
'build' => '(unknown)',
'build_date' => '(unknown)',
'base_build' => '(unknown)',
'base_build_date' => '(unknown)',
];
try {
if (file_exists('/var/www/counter-main.txt')) {
$return['build'] = trim(file_get_contents('/var/www/counter-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check build counter, but thats ok.');
Log::warning($e->getMessage());
}
try {
if (file_exists('/var/www/build-date-main.txt')) {
$return['build_date'] = trim(file_get_contents('/var/www/build-date-main.txt'));
}
} catch (Exception $e) { // generic catch for open basedir.
Log::debug('Could not check build date, but thats ok.');
Log::warning($e->getMessage());
}
if ('' !== (string)env('BASE_IMAGE_BUILD')) {
$return['base_build'] = env('BASE_IMAGE_BUILD');
}
if ('' !== (string)env('BASE_IMAGE_DATE')) {
$return['base_build_date'] = env('BASE_IMAGE_DATE');
}
return $return;
}
/**
* @return array
*/
private function getAppInfo(): array
{
$userGuard = config('auth.defaults.guard');
return [
'tz' => env('TZ'),
'debug' => var_export(config('app.debug'), true),
'log_channel' => env('LOG_CHANNEL'),
'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
'default_language' => (string)config('firefly.default_language'),
'default_locale' => (string)config('firefly.default_locale'),
'remote_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_header') : 'N/A',
'remote_mail_header' => $userGuard === 'remote_user_guard' ? config('auth.guard_email') : 'N/A',
'stateful_domains' => join(', ', config('sanctum.stateful')),
];
}
/**
* @return array
* @throws ContainerExceptionInterface
* @throws NotFoundExceptionInterface
*/
private function getuserInfo(): array
{
$userFlags = $this->getUserFlags();
// user info
$userAgent = request()->header('user-agent');
// set languages, see what happens:
$original = setlocale(LC_ALL, 0);
$localeAttempts = [];
$parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) {
$code = trim($code);
Log::debug(sprintf('Trying to set %s', $code));
$result = setlocale(LC_ALL, $code);
$localeAttempts[$code] = $result === $code;
}
setlocale(LC_ALL, $original);
return [
'user_id' => auth()->user()->id,
'user_count' => User::count(),
'user_flags' => $userFlags,
'user_agent' => $userAgent,
'locale_attempts' => $localeAttempts,
'locale' => app('steam')->getLocale(),
'language' => app('steam')->getLanguage(),
'view_range' => app('preferences')->get('viewRange', '1M')->data,
];
}
/**
* @return string
*/
private function getUserFlags(): string
{
$flags = [];
/** @var User $user */
$user = auth()->user();
// has liabilities
if ($user->accounts()->accountTypeIn([AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE])->count() > 0) {
$flags[] = ':credit_card:';
}
// has piggies
if ($user->piggyBanks()->count() > 0) {
$flags[] = ':pig:';
}
// has stored reconciliations
$type = TransactionType::whereType(TransactionType::RECONCILIATION)->first();
if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count()) {
$flags[] = ':ledger:';
}
// has used importer?
// has rules
if ($user->rules()->count() > 0) {
$flags[] = ':wrench:';
}
// has recurring transactions
if ($user->recurrences()->count() > 0) {
$flags[] = ':clock130:';
}
// has groups
if ($user->objectGroups()->count() > 0) {
$flags[] = ':bookmark_tabs:';
}
// uses bills
if ($user->bills()->count() > 0) {
$flags[] = ':email:';
}
return join(' ', $flags);
}
/**
@ -275,4 +342,5 @@ class DebugController extends Controller
return redirect(route('home'));
}
}

View File

@ -0,0 +1,58 @@
<?php
/**
* AccountList.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\Binder;
use FireflyIII\Models\Bill;
use FireflyIII\User;
use Illuminate\Routing\Route;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class UserGroupBill.
*/
class UserGroupBill implements BinderInterface
{
/**
* @param string $value
* @param Route $route
*
* @return Bill
* @throws NotFoundHttpException
*
*/
public static function routeBinder(string $value, Route $route): Bill
{
if (auth()->check()) {
/** @var User $user */
$user = auth()->user();
$currency = Bill::where('id', (int)$value)
->where('user_group_id', $user->user_group_id)
->first();
if (null !== $currency) {
return $currency;
}
}
throw new NotFoundHttpException();
}
}

View File

@ -28,9 +28,11 @@ use Carbon\CarbonInterface;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Note;
use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Log;
@ -40,6 +42,7 @@ use Log;
*/
class BillTransformer extends AbstractTransformer
{
private ExchangeRateConverter $converter;
private array $currencies;
private TransactionCurrency $default;
private array $groups;
@ -102,21 +105,71 @@ class BillTransformer extends AbstractTransformer
];
}
$this->default = app('amount')->getDefaultCurrency();
$this->default = app('amount')->getDefaultCurrency();
$this->converter = new ExchangeRateConverter();
// grab all paid dates:
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$journals = TransactionJournal::whereIn('bill_id', $bills)
->where('date', '>=', $this->parameters->get('start'))
->where('date', '<=', $this->parameters->get('end'))
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']);
$journals = TransactionJournal::whereIn('bill_id', $bills)
->where('date', '>=', $this->parameters->get('start'))
->where('date', '<=', $this->parameters->get('end'))
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']);
$journalIds = $journals->pluck('id')->toArray();
// grab transactions for amount:
$set = Transaction::whereIn('transaction_journal_id', $journalIds)
->where('transactions.amount', '<', 0)
->get(['transactions.id', 'transactions.transaction_journal_id', 'transactions.amount', 'transactions.foreign_amount', 'transactions.transaction_currency_id', 'transactions.foreign_currency_id']);
// convert to array for easy finding:
$transactions = [];
/** @var Transaction $transaction */
foreach ($set as $transaction) {
$journalId = (int)$transaction->transaction_journal_id;
$transactions[$journalId] = $transaction->toArray();
}
/** @var TransactionJournal $journal */
foreach ($journals as $journal) {
$billId = (int)$journal->bill_id;
$transaction = $transactions[(int)$journal->id] ?? [];
$billId = (int)$journal->bill_id;
$currencyId = (int)$transaction['transaction_currency_id'] ?? 0;
$currencies[$currencyId] = $currencies[$currencyId] ?? TransactionCurrency::find($currencyId);
// foreign currency
$foreignCurrencyId = null;
$foreignCurrencyCode = null;
$foreignCurrencyName = null;
$foreignCurrencySymbol = null;
$foreignCurrencyDp = null;
if (null !== $transaction['foreign_currency_id']) {
$foreignCurrencyId = (int)$transaction['foreign_currency_id'];
$currencies[$foreignCurrencyId] = $currencies[$foreignCurrencyId] ?? TransactionCurrency::find($foreignCurrencyId);
$foreignCurrencyCode = $currencies[$foreignCurrencyId]->code;
$foreignCurrencyName = $currencies[$foreignCurrencyId]->name;
$foreignCurrencySymbol = $currencies[$foreignCurrencyId]->symbol;
$foreignCurrencyDp = (int)$currencies[$foreignCurrencyId]->decimal_places;
}
$this->paidDates[$billId][] = [
'transaction_group_id' => (string)$journal->id,
'transaction_journal_id' => (string)$journal->transaction_group_id,
'date' => $journal->date->toAtomString(),
'transaction_group_id' => (string)$journal->id,
'transaction_journal_id' => (string)$journal->transaction_group_id,
'date' => $journal->date->toAtomString(),
'currency_id' => (int)$currencies[$currencyId]->id,
'currency_code' => $currencies[$currencyId]->code,
'currency_name' => $currencies[$currencyId]->name,
'currency_symbol' => $currencies[$currencyId]->symbol,
'currency_decimal_places' => (int)$currencies[$currencyId]->decimal_places,
'native_id' => (int)$currencies[$currencyId]->id,
'native_code' => $currencies[$currencyId]->code,
'native_symbol' => $currencies[$currencyId]->symbol,
'native_decimal_places' => (int)$currencies[$currencyId]->decimal_places,
'foreign_currency_id' => $foreignCurrencyId,
'foreign_currency_code' => $foreignCurrencyCode,
'foreign_currency_name' => $foreignCurrencyName,
'foreign_currency_symbol' => $foreignCurrencySymbol,
'foreign_currency_decimal_places' => $foreignCurrencyDp,
'amount' => $transaction['amount'],
'foreign_amount' => $transaction['foreign_amount'],
'native_amount' => $this->converter->convert($currencies[$currencyId], $this->default, $journal->date, $transaction['amount']),
'foreign_native_amount' => null === $transaction['foreign_amount'] ? null : $this->converter->convert($currencies[$foreignCurrencyId], $this->default, $journal->date, $transaction['foreign_amount']),
];
}
}
@ -131,11 +184,19 @@ class BillTransformer extends AbstractTransformer
*/
public function transform(Bill $bill): array
{
$paidData = $this->paidDates[(int)$bill->id] ?? [];
$nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[(int)$bill->id] ?? []);
$payDates = $this->payDates($bill);
$currency = $this->currencies[(int)$bill->transaction_currency_id];
$group = $this->groups[(int)$bill->id] ?? null;
$paidData = $this->paidDates[(int)$bill->id] ?? [];
$nextExpectedMatch = $this->nextExpectedMatch($bill, $this->paidDates[(int)$bill->id] ?? []);
$payDates = $this->payDates($bill);
$currency = $this->currencies[(int)$bill->transaction_currency_id];
$group = $this->groups[(int)$bill->id] ?? null;
// date for currency conversion
/** @var Carbon|null $startParam */
$startParam = $this->parameters->get('start');
/** @var Carbon|null $start */
$date = null === $startParam ? today() : clone $startParam;
$nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates);
return [
'id' => (int)$bill->id,
@ -144,10 +205,18 @@ class BillTransformer extends AbstractTransformer
'name' => $bill->name,
'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places),
'amount_max' => app('steam')->bcround($bill->amount_max, $currency->decimal_places),
'native_amount_min' => $this->converter->convert($currency, $this->default, $date, $bill->amount_min),
'native_amount_max' => $this->converter->convert($currency, $this->default, $date, $bill->amount_max),
'currency_id' => (string)$bill->transaction_currency_id,
'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol,
'currency_decimal_places' => (int)$currency->decimal_places,
'native_id' => $this->default->id,
'native_code' => $this->default->code,
'native_name' => $this->default->name,
'native_symbol' => $this->default->symbol,
'native_decimal_places' => (int)$this->default->decimal_places,
'date' => $bill->date->toAtomString(),
'end_date' => $bill->end_date?->toAtomString(),
'extension_date' => $bill->extension_date?->toAtomString(),
@ -185,8 +254,11 @@ class BillTransformer extends AbstractTransformer
// 2023-07-1 sub one day from the start date to fix a possible bug (see #7704)
// 2023-07-18 this particular date is used to search for the last paid date.
// 2023-07-18 the cloned $searchDate is used to grab the correct transactions.
/** @var Carbon $start */
$start = clone $this->parameters->get('start');
/** @var Carbon|null $startParam */
$startParam = $this->parameters->get('start');
/** @var Carbon|null $start */
$start = null === $startParam ? today() : clone $startParam;
$start->subDay();
$lastPaidDate = $this->lastPaidDate($dates, $start);

View File

@ -3,6 +3,16 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## 6.0.23 - 2023-09-04
### Changed
- New debug information tables are in HTML
### Fixed
- Remove extra slashes from paths, breaking CSS
## 6.0.22 - 2023-09-02
### API
@ -15,7 +25,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- Rules will now report failures if a Slack/Discord notification channel is configured
- Notifications can be sent to Discord
- Beta layout `v2`, activate with `FIRELY_III_LAYOUT=v2`
- Beta layout `v2`, activate with `FIREFLY_III_LAYOUT=v2`
### Changed

View File

@ -61,6 +61,7 @@ use FireflyIII\Support\Binder\JournalList;
use FireflyIII\Support\Binder\TagList;
use FireflyIII\Support\Binder\TagOrId;
use FireflyIII\Support\Binder\UserGroupAccount;
use FireflyIII\Support\Binder\UserGroupBill;
use FireflyIII\TransactionRules\Actions\AddTag;
use FireflyIII\TransactionRules\Actions\AppendDescription;
use FireflyIII\TransactionRules\Actions\AppendDescriptionToNotes;
@ -110,7 +111,7 @@ return [
'handle_debts' => true,
// see cer.php for exchange rates feature flag.
],
'version' => '6.0.22',
'version' => '6.0.23',
'api_version' => '2.0.7',
'db_version' => 20,
@ -480,6 +481,7 @@ return [
// V2 API endpoints:
'userGroupAccount' => UserGroupAccount::class,
'userGroupBill' => UserGroupBill::class,
],

View File

@ -159,7 +159,7 @@
</div>
</div>
{# bar to visualise spending in budget .#}
{# bar to visualise spending in budget . #}
<div class="row">
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<div class="progress spent_bar" data-id="{{ budget.id }}" data-budgeted="{{ budget.budgeted }}"
@ -223,7 +223,7 @@
{% if 1 == budget.budgeted|length %}
{% for budgetLimit in budget.budgeted %}
<a href="{{ route('budgets.show.limit', [budget.id, budgetLimit.id]) }}" data-id="{{ budget.id }}">{{ budget.name }}</a>
{% endfor %}
{% endfor %}
{% endif %}
{% if budget.budgeted|length > 1 %}
@ -233,15 +233,15 @@
{% endif %}
{% if budget.auto_budget %}
{% if 1 == budget.auto_budget.auto_budget_type %}
<span class="fa fa-fw fa-calendar-check-o" title="{{ 'auto_budget_reset_icon'|_ }}"></span>
{% endif %}
{% if 2 == budget.auto_budget.auto_budget_type %}
<span class="fa fa-fw fa-calendar-plus-o" title="{{ 'auto_budget_rollover_icon'|_ }}"></span>
{% endif %}
{% if 3 == budget.auto_budget.auto_budget_type %}
<span class="fa fa-fw fa-calendar-plus-o" title="{{ 'auto_budget_adjusted_icon'|_ }}"></span>
{% endif %}
{% if 1 == budget.auto_budget.auto_budget_type %}
<span class="fa fa-fw fa-calendar-check-o" title="{{ 'auto_budget_reset_icon'|_ }}"></span>
{% endif %}
{% if 2 == budget.auto_budget.auto_budget_type %}
<span class="fa fa-fw fa-calendar-plus-o" title="{{ 'auto_budget_rollover_icon'|_ }}"></span>
{% endif %}
{% if 3 == budget.auto_budget.auto_budget_type %}
<span class="fa fa-fw fa-calendar-plus-o" title="{{ 'auto_budget_adjusted_icon'|_ }}"></span>
{% endif %}
{% endif %}
{% if budget.attachments.count() > 0 %}
<span class="fa fa-paperclip"></span>
@ -253,11 +253,11 @@
<div class="input-group-addon">{{ defaultCurrency.symbol }}</div>
<input type="hidden" name="balance_currency_id" value="{{ defaultCurrency.id }}"/>
<input class="form-control budget_amount" data-original="0" data-id="{{ budget.id }}"
data-currency="{{ defaultCurrency.id }}" data-limit="0" value="0" autocomplete="off" min="0" name="amount"
type="number">
data-currency="{{ defaultCurrency.id }}" data-limit="0" value="0" autocomplete="off" min="0" name="amount"
type="number">
</div>
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
style="display:none;"></span>
style="display:none;"></span>
{% endif %}
{% if budget.budgeted|length > 0 %}
{% for budgetLimit in budget.budgeted %}
@ -270,9 +270,9 @@
<div class="input-group bl_entry" data-budget-limit-id="{{ budgetLimit.id }}">
<div class="input-group-addon">{{ budgetLimit.currency_symbol }}</div>
<input class="form-control budget_amount" data-original="{{ budgetLimit.amount }}"
data-id="{{ budget.id }}" data-limit="{{ budgetLimit.id }}" value="{{ budgetLimit.amount }}"
autocomplete="off"
min="0" name="amount" type="number">
data-id="{{ budget.id }}" data-limit="{{ budgetLimit.id }}" value="{{ budgetLimit.amount }}"
autocomplete="off"
min="0" name="amount" type="number">
<div class="input-group-btn">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true"
aria-expanded="false"><span class="caret"></span></button>
@ -284,7 +284,7 @@
</div>
</div>
<span class="text-danger budget_warning" data-id="{{ budget.id }}" data-budgetLimit="{{ budgetLimit.id }}"
style="display:none;"></span>
style="display:none;"></span>
{% endfor %}
{% endif %}
{% if budget.budgeted|length < currencies.count() %}
@ -305,14 +305,18 @@
{% endif %}
{% endfor %}
</td>
{# this cell displays the amount left in the budget, per budget limit. #}
<td class="left" data-id="{{ budget.id }}" style="text-align: right;">
{% for spentInfo in budget.spent %}
{% set countLimit = 0 %}
{% for budgetLimit in budget.budgeted %}
{# now looping a single budget limit. #}
{% if spentInfo.currency_id == budgetLimit.currency_id and budgetLimit.in_range %}
{# the code below is used for budget limits INSIDE the current view range. #}
{% set countLimit = countLimit + 1 %}
<span class="left_span" data-currency="{{ spentInfo.currency_id }}" data-limit="{{ budgetLimit.id }}"
data-value="{{ spentInfo.spent + budgetLimit.amount }}" class="amount_left">
data-value="{{ spentInfo.spent + budgetLimit.amount }}" class="amount_left">
{# the amount left is automatically calculated. #}
{{ formatAmountBySymbol(spentInfo.spent + budgetLimit.amount, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
{% if spentInfo.spent + budgetLimit.amount > 0 %}
({{ formatAmountBySymbol((spentInfo.spent + budgetLimit.amount) / activeDaysLeft, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
@ -324,8 +328,9 @@
{% endif %}
{% endfor %}
{% if countLimit == 0 %}
{# this code is used for budget limits OUTSIDE the current view range. #}
<span class="left_span" data-id="{{ budget.id }}" data-currency="{{ spentInfo.currency_id }}" data-limit="0"
class="amount_left" data-value="{{ spentInfo.spent }}">
class="amount_left" data-value="{{ spentInfo.spent }}">
{{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
</span>
<br/>
@ -333,14 +338,14 @@
{% endfor %}
{% for budgetLimit in budget.budgeted %}
{% if null == budget.spent[budgetLimit.currency_id] %}
<span class="left_span" data-currency="{{ spentInfo.currency_id }}" data-limit="{{ budgetLimit.id }}"
data-value="{{ spentInfo.spent + budgetLimit.amount }}" class="amount_left">
<span class="left_span" data-currency="{{ spentInfo.currency_id }}" data-limit="{{ budgetLimit.id }}"
data-value="{{ spentInfo.spent + budgetLimit.amount }}" class="amount_left">
{{ formatAmountBySymbol(budgetLimit.amount, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }}
{% if budgetLimit.in_range %}
({{ formatAmountBySymbol(budgetLimit.amount / activeDaysLeft, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
{% endif %}
{% if budgetLimit.in_range %}
({{ formatAmountBySymbol(budgetLimit.amount / activeDaysLeft, budgetLimit.currency_symbol, budgetLimit.currency_decimal_places) }})
{% endif %}
</span>
<br/>
<br/>
{% endif %}
{% endfor %}
</td>
@ -433,7 +438,7 @@
{% endblock %}
{% block styles %}
<link href="v1/css/bootstrap-sortable.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
<link href="v1/css/bootstrap-sortable.css?v={{ FF_VERSION }}" type="text/css" rel="stylesheet" media="all" nonce="{{ JS_NONCE }}">
{% endblock %}
{% block scripts %}

View File

@ -11,58 +11,11 @@
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;">
<strong>{{ trans('firefly.debug_pretty_table') }}</strong>
</p>
<textarea rows="30" cols="100" name="debug_info" id="debug_info" style="font-family:Menlo, Monaco, Consolas, monospace;font-size:8pt;">
Debug information generated at {{ now }} for Firefly III version **{{ FF_VERSION }}**.
<textarea rows="30" cols="100" name="debug_info" id="debug_info"
style="font-family:Menlo, Monaco, Consolas, monospace;font-size:8pt;">
Debug information generated at {{ now }} for Firefly III version **v{{ FF_VERSION }}**.
| Scope | Version |
| --- | --- |
| Firefly III | {{ FF_VERSION }} |
| Firefly III API | {{ config('firefly.api_version') }} |
{% if isDocker %}| Build | {{ buildNr }}, {{ buildDate }} |
| Base Build | {{ baseBuildNr }}, {{ baseBuildDate }} |
{% endif %}
| DB version | {{ foundDBversion }} (exp. {{ expectedDBversion}}) |
{% if not isDocker %}| Docker | No |
{% endif %}
| PHP | `{{ phpVersion }}` |
| Host | `{{ phpOs }}` |
| System info | Value |
| --- | --- |
| System TZ | {{ tz }} |
| Browser TZ | [BrowserTZ] |
| App environment | {{ appEnv }} |
| App debug mode | {{ appDebug }} |
| App cache driver | {{ cacheDriver }} |
| App logging | {{ appLogLevel }}, {{ logChannel }} |
| Display errors | {{ displayErrors }} |
| Error reporting | {{ errorReporting }} |
| Max upload | {{ uploadSize }} ({{ uploadSize|filesize }}) |
| Interface | {{ interface }} |
| Default language | {{ defaultLanguage }} |
| Default locale | {{ defaultLocale }} |
| BCscale | {{ bcscale }} |
| DB drivers | {{ drivers }} |
| Current driver | {{ currentDriver }} |
| Trusted proxies (.env) | `{{ trustedProxies }}` |
| User info | Value |
| --- | --- |
| Login provider | {{ loginProvider }} |
| User guard | {{ userGuard }} |
| Headers | {{ remoteHeader }}, {{ remoteMailHeader }} |
| Stateful domains | {{ stateful }} |
| Session start | {{ session('start') }} |
| Session end | {{ session('end') }} |
| Session first | {{ session('first') }} |
| User ID | {{ Auth.user.id }} |
| User language | {{ userLanguage }} |
| User locale | {{ userLocale }} |
{% for code,result in localeAttempts %}
| Attempt at "{{ code }}" | {{ result }} |
{% endfor %}
| User agent | {{ userAgent }} |
{{ table|escape('html') }}
</textarea>
<script type="text/javascript" nonce="{{ JS_NONCE }}">
var textArea = document.getElementById('debug_info');

View File

@ -0,0 +1,157 @@
<table>
<thead>
<tr>
<th colspan="2">System information</th>
</tr>
<tr>
<th>Item</th>
<th>Value</th>
</tr>
</thead>
<tbody>
{# Firefly III version #}
<tr>
<td>Firefly III</td>
<td>v{{ FF_VERSION }} / v{{ config('firefly.api_version') }} / {{ system.db_version }}
(exp. {{ config('firefly.db_version') }})
</td>
</tr>
{# PHP version + settings #}
<tr>
<td>PHP version</td>
<td>{{ system.php_version|escape }} / {{ system.interface }} / {{ system.php_os }}</td>
</tr>
<tr>
<td>BCscale</td>
<td>{{ system.bcscale }}</td>
</tr>
<tr>
<td>Error reporting</td>
<td>Display: {{ system.display_errors }}, reporting: {{ system.error_reporting }}</td>
</tr>
<tr>
<td>Max upload</td>
<td>{{ system.upload_size }} ({{ system.upload_size|filesize }})</td>
</tr>
<tr>
<td>Database drivers</td>
<td>
{% for driver in system.all_drivers %}{% if system.current_driver == driver %}<strong>*{{ driver }}*</strong>{% else %}{{ driver }}{% endif %}, {% endfor %}
</td>
</tr>
{# Docker information #}
{% if docker.is_docker %}
<tr>
<td>
Docker build
</td>
<td>
#{{ docker.build }}, base #{{ docker.base_build }}
</td>
</tr>
{% endif %}
</tbody>
</table>
<table>
<thead>
<tr>
<th colspan="2">Firefly III information</th>
</tr>
<tr>
<th>Item</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>Timezone</td>
<td>{{ app.tz }} + [BrowserTZ]</td>
</tr>
<tr>
<td>App environment</td>
<td>{{ config('app.env') }}, debug: {{ app.debug }}</td>
</tr>
<tr>
<td>Logging</td>
<td>{{ config('logging.level') }}, {{ app.log_channel }} / {{ app.audit_log_channel }}</td>
</tr>
<tr>
<td>Cache driver</td>
<td>{{ config('cache.default') }}</td>
</tr>
<tr>
<td>Default language and locale</td>
<td>{{ app.default_language }} + {{ app.default_locale }}</td>
</tr>
<tr>
<td>Trusted proxies</td>
<td>{{ config('firefly.trusted_proxies')|escape }}</td>
</tr>
<tr>
<td>Login provider & user guard</td>
<td>{{ config('auth.providers.users.driver') }} / {{ config('auth.defaults.guard') }}</td>
</tr>
<tr>
<td>Login headers</td>
<td>{{ app.remote_header }} + {{ app.remote_mail_header }}</td>
</tr>
<tr>
<td>Stateful domains</td>
<td>{{ app.stateful_domains }}</td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th colspan="2">User-specific information</th>
</tr>
<tr>
<th>Item</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>User</td>
<td><span>#</span>{{ user.user_id }} of {{ user.user_count }}</td>
</tr>
<tr>
<td>User flags</td>
<td>{{ user.user_flags }}</td>
</tr>
<tr>
<td>Session start</td>
<td>{{ session('start') }}</td>
</tr>
<tr>
<td>Session end</td>
<td>{{ session('end') }}</td>
</tr>
<tr>
<td>View range</td>
<td>{{ user.view_range }}</td>
</tr>
<tr>
<td>User language</td>
<td>{{ user.language }}</td>
</tr>
<tr>
<td>User locale</td>
<td>{{ user.locale }}</td>
</tr>
<tr>
<td>Locale(s) supported</td>
<td>
{% for key, attempt in user.locale_attempts %}
{{ key }}: {% if attempt %}:white_check_mark:{% else %}:x:{% endif %}<br>
{% endfor %}
</td>
</tr>
<tr>
<td>User agent</td>
<td>{{ user.user_agent }}</td>
</tr>
</tbody>
</table>

View File

@ -134,6 +134,7 @@ Route::group(
],
static function () {
Route::get('', ['uses' => 'ShowController@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']);
}