Update files for new release

This commit is contained in:
James Cole
2023-09-03 17:38:54 +02:00
parent 9ea3c4224e
commit ed1fdf9382
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; namespace FireflyIII\Api\V2\Controllers\Model\Bill;
use FireflyIII\Api\V2\Controllers\Controller; use FireflyIII\Api\V2\Controllers\Controller;
use FireflyIII\Models\Bill;
use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Administration\Bill\BillRepositoryInterface;
use FireflyIII\Transformers\V2\AccountTransformer;
use FireflyIII\Transformers\V2\BillTransformer; use FireflyIII\Transformers\V2\BillTransformer;
use Illuminate\Http\JsonResponse; use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -71,4 +73,17 @@ class ShowController extends Controller
->json($this->jsonApiList('subscriptions', $paginator, $transformer)) ->json($this->jsonApiList('subscriptions', $paginator, $transformer))
->header('Content-Type', self::CONTENT_TYPE); ->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

@@ -227,6 +227,7 @@ class IndexController extends Controller
Log::debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name)); Log::debug(sprintf('Working on budget #%d ("%s")', $current->id, $current->name));
$array = $current->toArray(); $array = $current->toArray();
$array['spent'] = []; $array['spent'] = [];
$array['spent_total'] = [];
$array['budgeted'] = []; $array['budgeted'] = [];
$array['attachments'] = $this->repository->getAttachments($current); $array['attachments'] = $this->repository->getAttachments($current);
$array['auto_budget'] = $this->repository->getAutoBudget($current); $array['auto_budget'] = $this->repository->getAutoBudget($current);
@@ -235,9 +236,10 @@ class IndexController extends Controller
foreach ($budgetLimits as $limit) { foreach ($budgetLimits as $limit) {
Log::debug(sprintf('Working on budget limit #%d', $limit->id)); Log::debug(sprintf('Working on budget limit #%d', $limit->id));
$currency = $limit->transactionCurrency ?? $defaultCurrency; $currency = $limit->transactionCurrency ?? $defaultCurrency;
$amount = app('steam')->bcround($limit->amount, $currency->decimal_places);
$array['budgeted'][] = [ $array['budgeted'][] = [
'id' => $limit->id, 'id' => $limit->id,
'amount' => app('steam')->bcround($limit->amount, $currency->decimal_places), 'amount' => $amount,
'start_date' => $limit->start_date->isoFormat($this->monthAndDayFormat), 'start_date' => $limit->start_date->isoFormat($this->monthAndDayFormat),
'end_date' => $limit->end_date->isoFormat($this->monthAndDayFormat), 'end_date' => $limit->end_date->isoFormat($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end), '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_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places, '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 */ /** @var TransactionCurrency $currency */

View File

@@ -28,7 +28,10 @@ use Exception;
use FireflyConfig; use FireflyConfig;
use FireflyIII\Exceptions\FireflyException; use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Middleware\IsDemoUser; use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Support\Http\Controllers\GetConfigurationData; use FireflyIII\Support\Http\Controllers\GetConfigurationData;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory; use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse; use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -37,6 +40,8 @@ use Illuminate\Support\Facades\Artisan;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Illuminate\View\View; use Illuminate\View\View;
use Monolog\Handler\RotatingFileHandler; use Monolog\Handler\RotatingFileHandler;
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;
/** /**
* Class DebugController * Class DebugController
@@ -117,86 +122,8 @@ class DebugController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
// basic scope information: $table = $this->generateTable();
$now = now(config('app.timezone'))->format('Y-m-d H:i:s e'); $now = now(config('app.timezone'))->format('Y-m-d H:i:s');
$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);
// get latest log file: // get latest log file:
$logger = Log::driver(); $logger = Log::driver();
@@ -213,50 +140,190 @@ class DebugController extends Controller
} }
if ('' !== $logContent) { if ('' !== $logContent) {
// last few lines // last few lines
$logContent = 'Truncated from this point <----|' . substr($logContent, -8192); $logContent = 'Truncated from this point <----|' . substr($logContent, -16384);
} }
return view( return view('debug', compact('table', 'now', 'logContent'));
'debug', }
compact(
'phpVersion', /**
'localeAttempts', * @return string
'expectedDBversion', */
'foundDBversion', private function generateTable(): string
'appEnv', {
'appDebug', // system information:
'logChannel', $system = $this->getSystemInformation();
'stateful', $docker = $this->getBuildInfo();
'tz', $app = $this->getAppInfo();
'uploadSize', $user = $this->getuserInfo();
'appLogLevel',
'remoteHeader', return (string)view('partials.debug-table', compact('system', 'docker', 'app', 'user'));
'remoteMailHeader', }
'now',
'drivers', /**
'currentDriver', * @return array
'userGuard', */
'loginProvider', private function getSystemInformation(): array
'buildNr', {
'buildDate', $maxFileSize = app('steam')->phpBytes(ini_get('upload_max_filesize'));
'baseBuildNr', $maxPostSize = app('steam')->phpBytes(ini_get('post_max_size'));
'baseBuildDate', $drivers = DB::availableDrivers();
'bcscale', $currentDriver = DB::getDriverName();
'userAgent', return [
'displayErrors', 'db_version' => app('fireflyconfig')->get('db_version', 1)->data,
'errorReporting', 'php_version' => PHP_VERSION,
'phpOs', 'php_os' => PHP_OS,
'interface', 'interface' => PHP_SAPI,
'logContent', 'bcscale' => bcscale(),
'cacheDriver', 'display_errors' => ini_get('display_errors'),
'trustedProxies', 'error_reporting' => $this->errorReporting((int)ini_get('error_reporting')),
'userLanguage', 'upload_size' => min($maxFileSize, $maxPostSize),
'userLocale', 'all_drivers' => $drivers,
'defaultLanguage', 'current_driver' => $currentDriver,
'defaultLocale', ];
'isDocker' }
)
); /**
* @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')); 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\Bill;
use FireflyIII\Models\Note; use FireflyIII\Models\Note;
use FireflyIII\Models\ObjectGroup; use FireflyIII\Models\ObjectGroup;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Support\Http\Api\ExchangeRateConverter;
use Illuminate\Support\Collection; use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use Log; use Log;
@@ -40,6 +42,7 @@ use Log;
*/ */
class BillTransformer extends AbstractTransformer class BillTransformer extends AbstractTransformer
{ {
private ExchangeRateConverter $converter;
private array $currencies; private array $currencies;
private TransactionCurrency $default; private TransactionCurrency $default;
private array $groups; private array $groups;
@@ -103,20 +106,70 @@ class BillTransformer extends AbstractTransformer
} }
$this->default = app('amount')->getDefaultCurrency(); $this->default = app('amount')->getDefaultCurrency();
$this->converter = new ExchangeRateConverter();
// grab all paid dates: // grab all paid dates:
if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) { if (null !== $this->parameters->get('start') && null !== $this->parameters->get('end')) {
$journals = TransactionJournal::whereIn('bill_id', $bills) $journals = TransactionJournal::whereIn('bill_id', $bills)
->where('date', '>=', $this->parameters->get('start')) ->where('date', '>=', $this->parameters->get('start'))
->where('date', '<=', $this->parameters->get('end')) ->where('date', '<=', $this->parameters->get('end'))
->get(['transaction_journals.id', 'transaction_journals.transaction_group_id', 'transaction_journals.date', 'transaction_journals.bill_id']); ->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 */ /** @var TransactionJournal $journal */
foreach ($journals as $journal) { foreach ($journals as $journal) {
$transaction = $transactions[(int)$journal->id] ?? [];
$billId = (int)$journal->bill_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][] = [ $this->paidDates[$billId][] = [
'transaction_group_id' => (string)$journal->id, 'transaction_group_id' => (string)$journal->id,
'transaction_journal_id' => (string)$journal->transaction_group_id, 'transaction_journal_id' => (string)$journal->transaction_group_id,
'date' => $journal->date->toAtomString(), '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']),
]; ];
} }
} }
@@ -136,6 +189,14 @@ class BillTransformer extends AbstractTransformer
$payDates = $this->payDates($bill); $payDates = $this->payDates($bill);
$currency = $this->currencies[(int)$bill->transaction_currency_id]; $currency = $this->currencies[(int)$bill->transaction_currency_id];
$group = $this->groups[(int)$bill->id] ?? null; $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); $nextExpectedMatchDiff = $this->getNextExpectedMatchDiff($nextExpectedMatch, $payDates);
return [ return [
'id' => (int)$bill->id, 'id' => (int)$bill->id,
@@ -144,10 +205,18 @@ class BillTransformer extends AbstractTransformer
'name' => $bill->name, 'name' => $bill->name,
'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places), 'amount_min' => app('steam')->bcround($bill->amount_min, $currency->decimal_places),
'amount_max' => app('steam')->bcround($bill->amount_max, $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_id' => (string)$bill->transaction_currency_id,
'currency_code' => $currency->code, 'currency_code' => $currency->code,
'currency_name' => $currency->name,
'currency_symbol' => $currency->symbol, 'currency_symbol' => $currency->symbol,
'currency_decimal_places' => (int)$currency->decimal_places, '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(), 'date' => $bill->date->toAtomString(),
'end_date' => $bill->end_date?->toAtomString(), 'end_date' => $bill->end_date?->toAtomString(),
'extension_date' => $bill->extension_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-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 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. // 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(); $start->subDay();
$lastPaidDate = $this->lastPaidDate($dates, $start); $lastPaidDate = $this->lastPaidDate($dates, $start);

View File

@@ -3,6 +3,16 @@
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.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 ## 6.0.22 - 2023-09-02
### API ### 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 - Rules will now report failures if a Slack/Discord notification channel is configured
- Notifications can be sent to Discord - 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 ### Changed

View File

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

View File

@@ -305,14 +305,18 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
</td> </td>
{# this cell displays the amount left in the budget, per budget limit. #}
<td class="left" data-id="{{ budget.id }}" style="text-align: right;"> <td class="left" data-id="{{ budget.id }}" style="text-align: right;">
{% for spentInfo in budget.spent %} {% for spentInfo in budget.spent %}
{% set countLimit = 0 %} {% set countLimit = 0 %}
{% for budgetLimit in budget.budgeted %} {% for budgetLimit in budget.budgeted %}
{# now looping a single budget limit. #}
{% if spentInfo.currency_id == budgetLimit.currency_id and budgetLimit.in_range %} {% 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 %} {% set countLimit = countLimit + 1 %}
<span class="left_span" data-currency="{{ spentInfo.currency_id }}" data-limit="{{ budgetLimit.id }}" <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) }} {{ formatAmountBySymbol(spentInfo.spent + budgetLimit.amount, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}
{% if spentInfo.spent + budgetLimit.amount > 0 %} {% if spentInfo.spent + budgetLimit.amount > 0 %}
({{ formatAmountBySymbol((spentInfo.spent + budgetLimit.amount) / activeDaysLeft, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}) ({{ formatAmountBySymbol((spentInfo.spent + budgetLimit.amount) / activeDaysLeft, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }})
@@ -324,6 +328,7 @@
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if countLimit == 0 %} {% 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" <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) }} {{ formatAmountBySymbol(spentInfo.spent, spentInfo.currency_symbol, spentInfo.currency_decimal_places) }}

View File

@@ -11,58 +11,11 @@
<p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;"> <p style="font-family:Arial, Arial, Helvetica, sans-serif;font-size:12pt;width:600px;">
<strong>{{ trans('firefly.debug_pretty_table') }}</strong> <strong>{{ trans('firefly.debug_pretty_table') }}</strong>
</p> </p>
<textarea rows="30" cols="100" name="debug_info" id="debug_info" style="font-family:Menlo, Monaco, Consolas, monospace;font-size:8pt;"> <textarea rows="30" cols="100" name="debug_info" id="debug_info"
Debug information generated at {{ now }} for Firefly III version **{{ FF_VERSION }}**. style="font-family:Menlo, Monaco, Consolas, monospace;font-size:8pt;">
Debug information generated at {{ now }} for Firefly III version **v{{ FF_VERSION }}**.
| Scope | Version | {{ table|escape('html') }}
| --- | --- |
| 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 }} |
</textarea> </textarea>
<script type="text/javascript" nonce="{{ JS_NONCE }}"> <script type="text/javascript" nonce="{{ JS_NONCE }}">
var textArea = document.getElementById('debug_info'); 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 () { static function () {
Route::get('', ['uses' => 'ShowController@index', 'as' => 'index']); 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/paid', ['uses' => 'SumController@paid', 'as' => 'sum.paid']);
Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']); Route::get('sum/unpaid', ['uses' => 'SumController@unpaid', 'as' => 'sum.unpaid']);
} }