Clean up some code.

This commit is contained in:
James Cole 2024-12-14 05:45:54 +01:00
parent b930ad4da7
commit 7d8d773f8f
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
78 changed files with 2760 additions and 2674 deletions

View File

@ -66,9 +66,9 @@ class PiggyBankController extends Controller
*/
public function piggyBanks(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit'));
$response = [];
$data = $request->getData();
$piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit'));
$response = [];
/** @var PiggyBank $piggy */
foreach ($piggies as $piggy) {
@ -96,9 +96,9 @@ class PiggyBankController extends Controller
*/
public function piggyBanksWithBalance(AutocompleteRequest $request): JsonResponse
{
$data = $request->getData();
$piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit'));
$response = [];
$data = $request->getData();
$piggies = $this->piggyRepository->searchPiggyBank($data['query'], $this->parameters->get('limit'));
$response = [];
/** @var PiggyBank $piggy */
foreach ($piggies as $piggy) {

View File

@ -63,8 +63,8 @@ class DestroyController extends Controller
*/
public function destroy(DestroyRequest $request): JsonResponse
{
$objects = $request->getObjects();
$this->unused = $request->boolean('unused', false);
$objects = $request->getObjects();
$this->unused = $request->boolean('unused', false);
$allExceptAssets = [AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::RECONCILIATION, AccountType::REVENUE];
$all = [AccountType::ASSET, AccountType::BENEFICIARY, AccountType::CASH, AccountType::CREDITCARD, AccountType::DEBT, AccountType::DEFAULT, AccountType::EXPENSE, AccountType::IMPORT, AccountType::INITIAL_BALANCE, AccountType::LIABILITY_CREDIT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::RECONCILIATION];
@ -101,11 +101,11 @@ class DestroyController extends Controller
private function destroyBudgets(): void
{
/** @var AvailableBudgetRepositoryInterface $abRepository */
$abRepository = app(AvailableBudgetRepositoryInterface::class);
$abRepository = app(AvailableBudgetRepositoryInterface::class);
$abRepository->destroyAll();
/** @var BudgetLimitRepositoryInterface $blRepository */
$blRepository = app(BudgetLimitRepositoryInterface::class);
$blRepository = app(BudgetLimitRepositoryInterface::class);
$blRepository->destroyAll();
/** @var BudgetRepositoryInterface $budgetRepository */

View File

@ -53,7 +53,7 @@ class PurgeController extends Controller
public function purge(): JsonResponse
{
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
// some manual code, too lazy to call all repositories.
@ -67,14 +67,14 @@ class PurgeController extends Controller
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($user);
$repository->purgeAll();
// $set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id')
// ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*'])
// ;
//
// /** @var PiggyBank $piggy */
// foreach ($set as $piggy) {
// $piggy->forceDelete();
// }
// $set = PiggyBank::leftJoin('accounts', 'accounts.id', 'piggy_banks.account_id')
// ->where('accounts.user_id', $user->id)->onlyTrashed()->get(['piggy_banks.*'])
// ;
//
// /** @var PiggyBank $piggy */
// foreach ($set as $piggy) {
// $piggy->forceDelete();
// }
// rule group
RuleGroup::whereUserId($user->id)->onlyTrashed()->forceDelete();

View File

@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use Illuminate\Http\JsonResponse;

View File

@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use Illuminate\Http\JsonResponse;
/**

View File

@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\JsonResponse;

View File

@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use Illuminate\Http\JsonResponse;
/**

View File

@ -28,7 +28,6 @@ use FireflyIII\Api\V1\Controllers\Controller;
use FireflyIII\Api\V1\Requests\Insight\GenericRequest;
use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Tag\TagRepositoryInterface;
use Illuminate\Http\JsonResponse;

View File

@ -72,14 +72,14 @@ class StoreController extends Controller
$data['notes'] = $data['notes'];
$data['budget_id'] = $budget->id;
$budgetLimit = $this->blRepository->store($data);
$manager = $this->getManager();
$budgetLimit = $this->blRepository->store($data);
$manager = $this->getManager();
/** @var BudgetLimitTransformer $transformer */
$transformer = app(BudgetLimitTransformer::class);
$transformer = app(BudgetLimitTransformer::class);
$transformer->setParameters($this->parameters);
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
$resource = new Item($budgetLimit, $transformer, 'budget_limits');
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', self::CONTENT_TYPE);
}

View File

@ -32,7 +32,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
use FireflyIII\Repositories\Budget\AvailableBudgetRepositoryInterface;

View File

@ -53,10 +53,11 @@ class UpdateRequest extends FormRequest
'currency_code' => ['currency_code', 'convertString'],
'notes' => ['notes', 'stringWithNewlines'],
];
if(false === $this->has('notes')) {
if (false === $this->has('notes')) {
// ignore notes, not submitted.
unset($fields['notes']);
}
return $this->getAllData($fields);
}

View File

@ -27,7 +27,6 @@ namespace FireflyIII\Api\V1\Requests\Models\PiggyBank;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Rules\IsValidPositiveAmount;
use FireflyIII\Rules\IsValidZeroOrMoreAmount;
use FireflyIII\Support\Request\ChecksLogin;
use FireflyIII\Support\Request\ConvertsDataTypes;
@ -96,9 +95,9 @@ class StoreRequest extends FormRequest
$validator->after(
function (Validator $validator): void {
// validate start before end only if both are there.
$data = $validator->getData();
$currency = $this->getCurrencyFromData($data);
$targetAmount = (string) ($data['target_amount'] ?? '0');
$data = $validator->getData();
$currency = $this->getCurrencyFromData($data);
$targetAmount = (string) ($data['target_amount'] ?? '0');
$currentAmount = '0';
if (array_key_exists('accounts', $data) && is_array($data['accounts'])) {
$repository = app(AccountRepositoryInterface::class);
@ -110,18 +109,18 @@ class StoreRequest extends FormRequest
// check currency here.
$accountCurrency = $repository->getAccountCurrency($account);
$isMultiCurrency = $repository->getMetaValue($account, 'is_multi_currency');
$currentAmount = bcadd($currentAmount, (string)($array['current_amount'] ?? '0'));
$currentAmount = bcadd($currentAmount, (string)($array['current_amount'] ?? '0'));
if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) {
$validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_currency'));
}
$type = $account->accountType->type;
$type = $account->accountType->type;
if (!in_array($type, $types, true)) {
$validator->errors()->add(sprintf('accounts.%d', $index), trans('validation.invalid_account_type'));
}
}
}
}
if(bccomp($targetAmount, $currentAmount) === -1 && bccomp($targetAmount, '0') === 1) {
if (-1 === bccomp($targetAmount, $currentAmount) && 1 === bccomp($targetAmount, '0')) {
$validator->errors()->add('target_amount', trans('validation.current_amount_too_much'));
}
}
@ -146,6 +145,7 @@ class StoreRequest extends FormRequest
'current_amount' => $this->clearString((string) ($entry['current_amount'] ?? '0')),
];
}
return $return;
}
@ -163,6 +163,7 @@ class StoreRequest extends FormRequest
return $currency;
}
}
throw new FireflyException('Unexpected empty currency.');
}
}

View File

@ -30,7 +30,6 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Support\Chart\ChartData;
use FireflyIII\Support\Http\Api\AccountBalanceGrouped;

View File

@ -31,7 +31,6 @@ use FireflyIII\Enums\TransactionTypeEnum;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Currency\CurrencyRepositoryInterface;
use FireflyIII\Support\Http\Api\CleansChartData;

View File

@ -33,7 +33,6 @@ use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Helpers\Report\NetWorthInterface;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\TransactionType;
use FireflyIII\Models\UserGroup;
use FireflyIII\Repositories\UserGroups\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\UserGroups\Bill\BillRepositoryInterface;

View File

@ -265,8 +265,8 @@ class ApplyRules extends Command
private function verifyInputDates(): void
{
// parse start date.
$inputStart = today(config('app.timezone'))->startOfMonth();
$startString = $this->option('start_date');
$inputStart = today(config('app.timezone'))->startOfMonth();
$startString = $this->option('start_date');
if (null === $startString) {
/** @var JournalRepositoryInterface $repository */
$repository = app(JournalRepositoryInterface::class);
@ -281,8 +281,8 @@ class ApplyRules extends Command
}
// parse end date
$inputEnd = today(config('app.timezone'));
$endString = $this->option('end_date');
$inputEnd = today(config('app.timezone'));
$endString = $this->option('end_date');
if (null !== $endString && '' !== $endString) {
$inputEnd = Carbon::createFromFormat('Y-m-d', $endString);
}
@ -297,7 +297,7 @@ class ApplyRules extends Command
}
$this->start_date = $inputStart;
$this->endDate = $inputEnd;
$this->endDate = $inputEnd;
}
private function grabAllRules(): void

View File

@ -1,4 +1,6 @@
<?php
declare(strict_types=1);
/*
* UpgradeMultiPiggyBanks.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -34,17 +36,15 @@ class UpgradeMultiPiggyBanks extends Command
public const string CONFIG_NAME = '620_make_multi_piggies';
protected $description = 'Upgrade piggybanks so they can use multiple accounts.';
protected $description = 'Upgrade piggybanks so they can use multiple accounts.';
protected $signature = 'firefly-iii:upgrade-multi-piggies {--F|force : Force the execution of this command.}';
protected $signature = 'firefly-iii:upgrade-multi-piggies {--F|force : Force the execution of this command.}';
private PiggyBankRepositoryInterface $repository;
private AccountRepositoryInterface $accountRepository;
/**
* Execute the console command.
*
* @return int
*/
public function handle(): int
{
@ -61,9 +61,6 @@ class UpgradeMultiPiggyBanks extends Command
return 0;
}
/**
* @return bool
*/
private function isExecuted(): bool
{
$configVar = app('fireflyconfig')->get(self::CONFIG_NAME, false);
@ -74,10 +71,6 @@ class UpgradeMultiPiggyBanks extends Command
return false;
}
/**
*
*/
private function markAsExecuted(): void
{
app('fireflyconfig')->set(self::CONFIG_NAME, true);
@ -89,6 +82,7 @@ class UpgradeMultiPiggyBanks extends Command
$this->accountRepository = app(AccountRepositoryInterface::class);
$set = PiggyBank::whereNotNull('account_id')->get();
Log::debug(sprintf('Will update %d piggy banks(s).', $set->count()));
/** @var PiggyBank $piggyBank */
foreach ($set as $piggyBank) {
$this->upgradePiggyBank($piggyBank);
@ -99,8 +93,8 @@ class UpgradeMultiPiggyBanks extends Command
{
$this->repository->setUser($piggyBank->account->user);
$this->accountRepository->setUser($piggyBank->account->user);
$repetition = $this->repository->getRepetition($piggyBank);
$currency = $this->accountRepository->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrencyByUserGroup($piggyBank->account->user->userGroup);
$repetition = $this->repository->getRepetition($piggyBank);
$currency = $this->accountRepository->getAccountCurrency($piggyBank->account) ?? app('amount')->getDefaultCurrencyByUserGroup($piggyBank->account->user->userGroup);
// update piggy bank to have a currency.
$piggyBank->transaction_currency_id = $currency->id;
@ -108,7 +102,7 @@ class UpgradeMultiPiggyBanks extends Command
// store current amount in account association.
$piggyBank->accounts()->sync([$piggyBank->account->id => ['current_amount' => $repetition->current_amount]]);
$piggyBank->account_id = null;
$piggyBank->account_id = null;
$piggyBank->save();
// remove all repetitions (no longer used)

View File

@ -43,7 +43,7 @@ class RegisteredUser extends Event
*/
public function __construct(OwnerNotifiable $owner, User $user)
{
$this->user = $user;
$this->user = $user;
$this->owner = $owner;
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* UnknownUserAttemptedLogin.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -24,7 +25,6 @@ declare(strict_types=1);
namespace FireflyIII\Events\Security;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
class UnknownUserAttemptedLogin
{

View File

@ -1,4 +1,5 @@
<?php
/*
* TestEmailChannel.php
* Copyright (c) 2024 james@firefly-iii.org.

View File

@ -43,7 +43,8 @@ use Illuminate\Support\Facades\Notification;
*/
class AdminEventHandler
{
public function sendLoginAttemptNotification(UnknownUserAttemptedLogin $event): void {
public function sendLoginAttemptNotification(UnknownUserAttemptedLogin $event): void
{
try {
$owner = new OwnerNotifiable();
Notification::send($owner, new UnknownUserLoginAttempt($event->address));
@ -64,13 +65,13 @@ class AdminEventHandler
}
}
public function sendInvitationNotification(InvitationCreated $event): void
{
$sendMail = app('fireflyconfig')->get('notification_invite_created', true)->data;
if (false === $sendMail) {
return;
}
try {
$owner = new OwnerNotifiable();
Notification::send($owner, new UserInvitation($owner, $event->invitee));
@ -131,18 +132,27 @@ class AdminEventHandler
switch ($event->channel) {
case 'email':
$class = TestNotificationEmail::class;
break;
case 'slack':
$class = TestNotificationSlack::class;
break;
case 'ntfy':
$class = TestNotificationNtfy::class;
break;
case 'pushover':
$class = TestNotificationPushover::class;
break;
default:
app('log')->error(sprintf('Unknown channel "%s" in sendTestNotification method.', $event->channel));
return;
}
Log::debug(sprintf('Will send %s as a notification.', $class));

View File

@ -82,8 +82,8 @@ class UserEventHandler
$repository = app(UserRepositoryInterface::class);
/** @var User $user */
$user = $event->user;
$count = $repository->count();
$user = $event->user;
$count = $repository->count();
// only act when there is 1 user in the system and he has no admin rights.
if (1 === $count && !$repository->hasRole($user, 'owner')) {
@ -115,13 +115,13 @@ class UserEventHandler
*/
public function createGroupMembership(RegisteredUser $event): void
{
$user = $event->user;
$groupExists = true;
$groupTitle = $user->email;
$index = 1;
$user = $event->user;
$groupExists = true;
$groupTitle = $user->email;
$index = 1;
/** @var UserGroup $group */
$group = null;
$group = null;
// create a new group.
while (true === $groupExists) { // @phpstan-ignore-line
@ -131,7 +131,7 @@ class UserEventHandler
break;
}
$groupTitle = sprintf('%s-%d', $user->email, $index);
$groupTitle = sprintf('%s-%d', $user->email, $index);
++$index;
if ($index > 99) {
throw new FireflyException('Email address can no longer be used for registrations.');
@ -139,7 +139,7 @@ class UserEventHandler
}
/** @var null|UserRole $role */
$role = UserRole::where('title', UserRoleEnum::OWNER->value)->first();
$role = UserRole::where('title', UserRoleEnum::OWNER->value)->first();
if (null === $role) {
throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?');
}
@ -165,7 +165,7 @@ class UserEventHandler
$repository = app(UserRepositoryInterface::class);
/** @var User $user */
$user = $event->user;
$user = $event->user;
if ($repository->hasRole($user, 'demo')) {
// set user back to English.
app('preferences')->setForUser($user, 'language', 'en_US');
@ -186,7 +186,7 @@ class UserEventHandler
return; // do not email demo user.
}
$list = app('preferences')->getForUser($user, 'login_ip_history', [])->data;
$list = app('preferences')->getForUser($user, 'login_ip_history', [])->data;
if (!is_array($list)) {
$list = [];
}
@ -223,6 +223,7 @@ class UserEventHandler
$sendMail = (bool) app('fireflyconfig')->get('notification_admin_new_reg', true)->data;
if ($sendMail) {
$owner = $event->owner;
try {
Notification::send($owner, new AdminRegistrationNotification($event->owner, $event->user));
} catch (\Exception $e) { // @phpstan-ignore-line
@ -369,7 +370,7 @@ class UserEventHandler
public function storeUserIPAddress(ActuallyLoggedIn $event): void
{
app('log')->debug('Now in storeUserIPAddress');
$user = $event->user;
$user = $event->user;
if ($user->hasRole('demo')) {
app('log')->debug('Do not log demo user logins');
@ -386,8 +387,8 @@ class UserEventHandler
return;
}
$inArray = false;
$ip = request()->ip();
$inArray = false;
$ip = request()->ip();
app('log')->debug(sprintf('User logging in from IP address %s', $ip));
// update array if in array
@ -415,7 +416,7 @@ class UserEventHandler
$preference = array_values($preference);
/** @var bool $send */
$send = app('preferences')->getForUser($user, 'notification_user_login', true)->data;
$send = app('preferences')->getForUser($user, 'notification_user_login', true)->data;
app('preferences')->setForUser($user, 'login_ip_history', $preference);
if (false === $inArray && true === $send) {

View File

@ -36,14 +36,14 @@ class PiggyBankObserver
{
app('log')->debug('Observe "created" of a piggy bank. DO NOTHING.');
// $repetition = new PiggyBankRepetition();
// $repetition->piggyBank()->associate($piggyBank);
// $repetition->start_date = $piggyBank->start_date;
// $repetition->start_date_tz = $piggyBank->start_date->format('e');
// $repetition->target_date = $piggyBank->target_date;
// $repetition->target_date_tz = $piggyBank->target_date?->format('e');
// $repetition->current_amount = '0';
// $repetition->save();
// $repetition = new PiggyBankRepetition();
// $repetition->piggyBank()->associate($piggyBank);
// $repetition->start_date = $piggyBank->start_date;
// $repetition->start_date_tz = $piggyBank->start_date->format('e');
// $repetition->target_date = $piggyBank->target_date;
// $repetition->target_date_tz = $piggyBank->target_date?->format('e');
// $repetition->current_amount = '0';
// $repetition->save();
}
/**

View File

@ -23,15 +23,9 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Admin;
use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Support\Notifications\UrlValidator;
use FireflyIII\User;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;

View File

@ -1,4 +1,5 @@
<?php
/*
* NotificationController.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -36,25 +37,25 @@ class NotificationController extends Controller
public function index()
{
Log::channel('audit')->info('User visits notifications index.');
$title = (string) trans('firefly.administration');
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = (string) trans('firefly.title_owner_notifications');
$subTitleIcon = 'envelope-o';
$title = (string) trans('firefly.administration');
$mainTitleIcon = 'fa-hand-spock-o';
$subTitle = (string) trans('firefly.title_owner_notifications');
$subTitleIcon = 'envelope-o';
// notification settings:
$slackUrl = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
$pushoverAppToken = app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data;
$pushoverUserToken = app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data;
$ntfyServer = app('fireflyconfig')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data;
$ntfyTopic = app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data;
$ntfyAuth = app('fireflyconfig')->get('ntfy_auth', false)->data;
$ntfyUser = app('fireflyconfig')->getEncrypted('ntfy_user', '')->data;
$ntfyPass = app('fireflyconfig')->getEncrypted('ntfy_pass', '')->data;
$channels = config('notifications.channels');
$forcedAvailability = [];
$slackUrl = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
$pushoverAppToken = app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data;
$pushoverUserToken = app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data;
$ntfyServer = app('fireflyconfig')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data;
$ntfyTopic = app('fireflyconfig')->getEncrypted('ntfy_topic', '')->data;
$ntfyAuth = app('fireflyconfig')->get('ntfy_auth', false)->data;
$ntfyUser = app('fireflyconfig')->getEncrypted('ntfy_user', '')->data;
$ntfyPass = app('fireflyconfig')->getEncrypted('ntfy_pass', '')->data;
$channels = config('notifications.channels');
$forcedAvailability = [];
// admin notification settings:
$notifications = [];
$notifications = [];
foreach (config('notifications.notifications.owner') as $key => $info) {
if ($info['enabled']) {
$notifications[$key] = app('fireflyconfig')->get(sprintf('notification_%s', $key), true)->data;
@ -68,18 +69,31 @@ class NotificationController extends Controller
$forcedAvailability['ntfy'] = '' !== $ntfyTopic;
$forcedAvailability['pushover'] = '' !== $pushoverAppToken && '' !== $pushoverUserToken;
return view('admin.notifications.index',
compact(
'title', 'subTitle', 'forcedAvailability', 'mainTitleIcon', 'subTitleIcon', 'channels',
'slackUrl', 'notifications',
'pushoverAppToken', 'pushoverUserToken',
'ntfyServer', 'ntfyTopic', 'ntfyAuth', 'ntfyUser', 'ntfyPass'
));
return view(
'admin.notifications.index',
compact(
'title',
'subTitle',
'forcedAvailability',
'mainTitleIcon',
'subTitleIcon',
'channels',
'slackUrl',
'notifications',
'pushoverAppToken',
'pushoverUserToken',
'ntfyServer',
'ntfyTopic',
'ntfyAuth',
'ntfyUser',
'ntfyPass'
)
);
}
public function postIndex(NotificationRequest $request): RedirectResponse
{
$all = $request->getAll();
$all = $request->getAll();
foreach (config('notifications.notifications.owner') as $key => $info) {
if (array_key_exists($key, $all)) {
@ -112,7 +126,9 @@ class NotificationController extends Controller
switch ($channel) {
default:
session()->flash('error', (string) trans('firefly.notification_test_failed', ['channel' => $channel]));
break;
case 'email':
case 'slack':
case 'pushover':

View File

@ -28,7 +28,6 @@ use FireflyIII\Events\ActuallyLoggedIn;
use FireflyIII\Events\Security\UnknownUserAttemptedLogin;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Providers\RouteServiceProvider;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use Illuminate\Contracts\Foundation\Application;
@ -70,7 +69,7 @@ class LoginController extends Controller
public function __construct()
{
parent::__construct();
$this->username = 'email';
$this->username = 'email';
$this->middleware('guest')->except('logout');
$this->repository = app(UserRepositoryInterface::class);
}
@ -128,7 +127,7 @@ class LoginController extends Controller
}
app('log')->warning('Login attempt failed.');
$username = (string) $request->get($this->username());
if(null === $this->repository->findByEmail($username)) {
if (null === $this->repository->findByEmail($username)) {
// send event to owner.
event(new UnknownUserAttemptedLogin($username));
}

View File

@ -95,7 +95,7 @@ class RegisterController extends Controller
$this->validator($request->all())->validate();
$user = $this->createUser($request->all());
app('log')->info(sprintf('Registered new user %s', $user->email));
$owner = new OwnerNotifiable();
$owner = new OwnerNotifiable();
event(new RegisteredUser($owner, $user));
$this->guard()->login($user);

View File

@ -84,7 +84,7 @@ class BudgetLimitController extends Controller
$budgetLimits = $this->blRepository->getBudgetLimits($budget, $start, $end);
// remove already budgeted currencies with the same date range
$currencies = $collection->filter(
$currencies = $collection->filter(
static function (TransactionCurrency $currency) use ($budgetLimits, $start, $end) {
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
@ -107,6 +107,7 @@ class BudgetLimitController extends Controller
public function show(BudgetLimit $budgetLimit)
{
$notes = $this->blRepository->getNoteText($budgetLimit);
return view('budgets.budget-limits.show', compact('budgetLimit', 'notes'));
}
@ -116,6 +117,7 @@ class BudgetLimitController extends Controller
public function edit(BudgetLimit $budgetLimit)
{
$notes = $this->blRepository->getNoteText($budgetLimit);
return view('budgets.budget-limits.edit', compact('budgetLimit', 'notes'));
}
@ -135,7 +137,7 @@ class BudgetLimitController extends Controller
*
* @throws FireflyException
*/
public function store(Request $request): JsonResponse | RedirectResponse
public function store(Request $request): JsonResponse|RedirectResponse
{
app('log')->debug('Going to store new budget-limit.', $request->all());
// first search for existing one and update it if necessary.
@ -144,14 +146,14 @@ class BudgetLimitController extends Controller
if (null === $currency || null === $budget) {
throw new FireflyException('No valid currency or budget.');
}
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
$start = Carbon::createFromFormat('Y-m-d', $request->get('start'));
$end = Carbon::createFromFormat('Y-m-d', $request->get('end'));
if (null === $start || null === $end) {
return response()->json([]);
}
$amount = (string) $request->get('amount');
$amount = (string) $request->get('amount');
$start->startOfDay();
$end->startOfDay();
@ -161,7 +163,7 @@ class BudgetLimitController extends Controller
app('log')->debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d'), $end->format('Y-m-d')));
$limit = $this->blRepository->find($budget, $currency, $start, $end);
$limit = $this->blRepository->find($budget, $currency, $start, $end);
// sanity check on amount:
if (0 === bccomp($amount, '0')) {
@ -195,25 +197,25 @@ class BudgetLimitController extends Controller
);
}
// parse notes, if any.
$notes = (string) $request->get('notes');
$notes = (string) $request->get('notes');
$this->blRepository->setNoteText($limit, $notes);
if ($request->expectsJson()) {
$array = $limit->toArray();
$array = $limit->toArray();
// add some extra metadata:
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency);
$array['spent'] = $spentArr[$currency->id]['sum'] ?? '0';
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount']));
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
$array['days_left'] = (string) $this->activeDaysLeft($start, $end);
$spentArr = $this->opsRepository->sumExpenses($limit->start_date, $limit->end_date, null, new Collection([$budget]), $currency);
$array['spent'] = $spentArr[$currency->id]['sum'] ?? '0';
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount']));
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
$array['days_left'] = (string) $this->activeDaysLeft($start, $end);
// left per day:
$array['left_per_day'] = 0 === bccomp('0', $array['days_left']) ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
$array['left_per_day'] = 0 === bccomp('0', $array['days_left']) ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
// left per day formatted.
$array['left_per_day_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $array['left_per_day']);
// notes:
$array['notes'] = $this->blRepository->getNoteText($limit);
$array['notes'] = $this->blRepository->getNoteText($limit);
return response()->json($array);
}
@ -223,7 +225,7 @@ class BudgetLimitController extends Controller
public function update(Request $request, BudgetLimit $budgetLimit): JsonResponse|RedirectResponse
{
$amount = (string) $request->get('amount');
$amount = (string) $request->get('amount');
if ('' === $amount) {
$amount = '0';
}
@ -235,7 +237,7 @@ class BudgetLimitController extends Controller
$budgetId = $budgetLimit->budget_id;
$currency = $budgetLimit->transactionCurrency;
$this->blRepository->destroyBudgetLimit($budgetLimit);
$array = [
$array = [
'budget_id' => $budgetId,
'left_formatted' => app('amount')->formatAnything($currency, '0'),
'left_per_day_formatted' => app('amount')->formatAnything($currency, '0'),
@ -248,29 +250,29 @@ class BudgetLimitController extends Controller
if (-1 === bccomp($amount, '0')) {
$amount = bcmul($amount, '-1');
}
$notes = (string)$request->get('notes');
if(strlen($notes) > 32768) {
$notes = (string)$request->get('notes');
if (strlen($notes) > 32768) {
$notes = substr($notes, 0, 32768);
}
$limit = $this->blRepository->update($budgetLimit, ['amount' => $amount,'notes' => $notes]);
$limit = $this->blRepository->update($budgetLimit, ['amount' => $amount, 'notes' => $notes]);
app('preferences')->mark();
$array = $limit->toArray();
$array = $limit->toArray();
$spentArr = $this->opsRepository->sumExpenses(
$spentArr = $this->opsRepository->sumExpenses(
$limit->start_date,
$limit->end_date,
null,
new Collection([$budgetLimit->budget]),
$budgetLimit->transactionCurrency
);
$daysLeft = $this->activeDaysLeft($limit->start_date, $limit->end_date);
$array['spent'] = $spentArr[$budgetLimit->transactionCurrency->id]['sum'] ?? '0';
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount']));
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
$array['days_left'] = (string) $daysLeft;
$array['left_per_day'] = 0 === $daysLeft ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
$daysLeft = $this->activeDaysLeft($limit->start_date, $limit->end_date);
$array['spent'] = $spentArr[$budgetLimit->transactionCurrency->id]['sum'] ?? '0';
$array['left_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, bcadd($array['spent'], $array['amount']));
$array['amount_formatted'] = app('amount')->formatAnything($limit->transactionCurrency, $limit['amount']);
$array['days_left'] = (string) $daysLeft;
$array['left_per_day'] = 0 === $daysLeft ? bcadd($array['spent'], $array['amount']) : bcdiv(bcadd($array['spent'], $array['amount']), $array['days_left']);
// left per day formatted.
$array['amount'] = app('steam')->bcround($limit['amount'], $limit->transactionCurrency->decimal_places);
@ -278,6 +280,7 @@ class BudgetLimitController extends Controller
if ('true' === $request->get('redirect')) {
return redirect(route('budgets.index'));
}
return response()->json($array);
}
}

View File

@ -213,7 +213,7 @@ class IndexController extends Controller
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => $amount,
'notes' => $this->blRepository->getNoteText($limit),
'notes' => $this->blRepository->getNoteText($limit),
'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),

View File

@ -43,7 +43,6 @@ use Illuminate\Support\Facades\Log;
*/
class HomeController extends Controller
{
/**
* HomeController constructor.
*/
@ -62,8 +61,8 @@ class HomeController extends Controller
*/
public function dateRange(Request $request): JsonResponse
{
$stringStart = '';
$stringEnd = '';
$stringStart = '';
$stringEnd = '';
try {
$stringStart = e((string) $request->get('start'));
@ -98,7 +97,7 @@ class HomeController extends Controller
app('log')->debug('Range is now marked as "custom".');
}
$diff = $start->diffInDays($end, true) + 1;
$diff = $start->diffInDays($end, true) + 1;
if ($diff > 366) {
$request->session()->flash('warning', (string) trans('firefly.warning_much_data', ['days' => (int) $diff]));
@ -153,13 +152,13 @@ class HomeController extends Controller
}
/** @var Carbon $start */
$start = session('start', today(config('app.timezone'))->startOfMonth());
$start = session('start', today(config('app.timezone'))->startOfMonth());
/** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth());
$accounts = $repository->getAccountsById($frontpageArray);
$today = today(config('app.timezone'));
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
$end = session('end', today(config('app.timezone'))->endOfMonth());
$accounts = $repository->getAccountsById($frontpageArray);
$today = today(config('app.timezone'));
$accounts = $accounts->sortBy('order'); // sort frontpage accounts by order
app('log')->debug('Frontpage accounts are ', $frontpageArray);
@ -169,14 +168,14 @@ class HomeController extends Controller
// collect groups for each transaction.
foreach ($accounts as $account) {
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector = app(GroupCollectorInterface::class);
$collector->setAccounts(new Collection([$account]))->withAccountInformation()->setRange($start, $end)->setLimit(10)->setPage(1);
$set = $collector->getExtractedJournals();
$transactions[] = ['transactions' => $set, 'account' => $account];
}
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
event(new RequestedVersionCheckStatus($user));
return view('index', compact('count', 'subTitle', 'transactions', 'billCount', 'start', 'end', 'today', 'pageTitle'));
@ -187,11 +186,11 @@ class HomeController extends Controller
$subTitle = (string) trans('firefly.welcome_back');
$pageTitle = (string) trans('firefly.main_dashboard_page_title');
$start = session('start', today(config('app.timezone'))->startOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth());
$start = session('start', today(config('app.timezone'))->startOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth());
/** @var User $user */
$user = auth()->user();
$user = auth()->user();
event(new RequestedVersionCheckStatus($user));
return view('index', compact('subTitle', 'start', 'end', 'pageTitle'));

View File

@ -94,7 +94,7 @@ class CreateController extends Controller
*/
public function store(PiggyBankStoreRequest $request)
{
$data = $request->getPiggyBankData();
$data = $request->getPiggyBankData();
if (null === $data['start_date']) {
$data['start_date'] = today(config('app.timezone'));
@ -107,7 +107,7 @@ class CreateController extends Controller
// store attachment(s):
/** @var null|array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($piggyBank, $files);
}
@ -119,7 +119,7 @@ class CreateController extends Controller
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments'));
}
$redirect = redirect($this->getPreviousUrl('piggy-banks.create.url'));
$redirect = redirect($this->getPreviousUrl('piggy-banks.create.url'));
if (1 === (int) $request->get('create_another')) {
session()->put('piggy-banks.create.fromStore', true);

View File

@ -81,13 +81,13 @@ class IndexController extends Controller
{
$this->cleanupObjectGroups();
$this->piggyRepos->resetOrder();
$collection = $this->piggyRepos->getPiggyBanks();
$collection = $this->piggyRepos->getPiggyBanks();
/** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth());
// transform piggies using the transformer:
$parameters = new ParameterBag();
$parameters = new ParameterBag();
$parameters->set('end', $end);
@ -96,10 +96,10 @@ class IndexController extends Controller
$accountTransformer->setParameters($parameters);
// data
$piggyBanks = $this->groupPiggyBanks($collection);
$accounts = $this->collectAccounts($collection);
$accounts = $this->mergeAccountsAndPiggies($piggyBanks, $accounts);
$piggyBanks = $this->makeSums($piggyBanks);
$piggyBanks = $this->groupPiggyBanks($collection);
$accounts = $this->collectAccounts($collection);
$accounts = $this->mergeAccountsAndPiggies($piggyBanks, $accounts);
$piggyBanks = $this->makeSums($piggyBanks);
ksort($piggyBanks);
@ -112,7 +112,7 @@ class IndexController extends Controller
foreach ($piggyBanks as $groupOrder => $group) {
$groupId = $group['object_group_id'];
foreach ($group['piggy_banks'] as $piggy) {
$currencyId = $piggy['currency_id'];
$currencyId = $piggy['currency_id'];
$sums[$groupId][$currencyId] ??= [
'target' => '0',
'saved' => '0',
@ -164,47 +164,51 @@ class IndexController extends Controller
/** @var PiggyBankTransformer $transformer */
$transformer = app(PiggyBankTransformer::class);
$transformer->setParameters(new ParameterBag());
$piggyBanks = [];
$piggyBanks = [];
/** @var PiggyBank $piggy */
foreach ($collection as $piggy) {
$array = $transformer->transform($piggy);
$groupOrder = (int) $array['object_group_order'];
$array = $transformer->transform($piggy);
$groupOrder = (int) $array['object_group_order'];
$piggyBanks[$groupOrder] ??= [
'object_group_id' => $array['object_group_id'] ?? 0,
'object_group_title' => $array['object_group_title'] ?? trans('firefly.default_group_title_name'),
'piggy_banks' => [],
];
$array['attachments'] = $this->piggyRepos->getAttachments($piggy);
$array['attachments'] = $this->piggyRepos->getAttachments($piggy);
// sum the total amount for the index.
$piggyBanks[$groupOrder]['piggy_banks'][] = $array;
}
return $piggyBanks;
}
private function collectAccounts(Collection $collection): array
{
/** @var Carbon $end */
$end = session('end', today(config('app.timezone'))->endOfMonth());
$end = session('end', today(config('app.timezone'))->endOfMonth());
// transform piggies using the transformer:
$parameters = new ParameterBag();
$parameters = new ParameterBag();
$parameters->set('end', $end);
/** @var AccountTransformer $accountTransformer */
$accountTransformer = app(AccountTransformer::class);
$accountTransformer->setParameters($parameters);
$return = [];
$return = [];
/** @var PiggyBank $piggy */
foreach ($collection as $piggy) {
$accounts = $piggy->accounts;
/** @var Account $account */
foreach ($accounts as $account) {
$array = $accountTransformer->transform($account);
$accountId = (int) $array['id'];
if (!array_key_exists($accountId, $return)) {
$return[$accountId] = $array;
$return[$accountId] = $array;
// add some interesting details:
$return[$accountId]['left'] = $return[$accountId]['current_balance'];
@ -214,20 +218,21 @@ class IndexController extends Controller
}
// calculate new interesting fields:
// $return[$accountId]['left'] -= $array['current_amount'];
// $return[$accountId]['saved'] += $array['current_amount'];
// $return[$accountId]['target'] += $array['target_amount'];
// $return[$accountId]['to_save'] += ($array['target_amount'] - $array['current_amount']);
// $return['account_name'] = $account['name'];
// $return[$accountId]['left'] -= $array['current_amount'];
// $return[$accountId]['saved'] += $array['current_amount'];
// $return[$accountId]['target'] += $array['target_amount'];
// $return[$accountId]['to_save'] += ($array['target_amount'] - $array['current_amount']);
// $return['account_name'] = $account['name'];
}
}
return $return;
}
private function mergeAccountsAndPiggies(array $piggyBanks, array $accounts): array
{
/** @var array $piggyBank */
// @var array $piggyBank
foreach ($piggyBanks as $group) {
foreach ($group['piggy_banks'] as $piggyBank) {
// loop all accounts in this piggy bank subtract the current amount from "left to save" in the $accounts array.
@ -243,6 +248,7 @@ class IndexController extends Controller
}
}
}
return $accounts;
}
}

View File

@ -66,14 +66,14 @@ class PreferencesController extends Controller
*/
public function index(AccountRepositoryInterface $repository)
{
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$isDocker = env('IS_DOCKER', false);
$groupedAccounts = [];
$accounts = $repository->getAccountsByType([AccountType::DEFAULT, AccountType::ASSET, AccountType::LOAN, AccountType::DEBT, AccountType::MORTGAGE]);
$isDocker = env('IS_DOCKER', false);
$groupedAccounts = [];
/** @var Account $account */
foreach ($accounts as $account) {
$type = $account->accountType->type;
$role = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role'));
$type = $account->accountType->type;
$role = sprintf('opt_group_%s', $repository->getMetaValue($account, 'account_role'));
if (in_array($type, [AccountType::MORTGAGE, AccountType::DEBT, AccountType::LOAN], true)) {
$role = sprintf('opt_group_l_%s', $type);
@ -94,42 +94,49 @@ class PreferencesController extends Controller
if (!is_array($frontpageAccounts)) {
$frontpageAccounts = $accountIds;
}
$language = app('steam')->getLanguage();
$languages = config('firefly.languages');
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
$darkMode = app('preferences')->get('darkMode', 'browser')->data;
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
$language = app('steam')->getLanguage();
$languages = config('firefly.languages');
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
$darkMode = app('preferences')->get('darkMode', 'browser')->data;
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
if (is_array($fiscalYearStartStr)) {
$fiscalYearStartStr = '01-01';
}
$fiscalYearStart = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr);
$tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$availableDarkModes = config('firefly.available_dark_modes');
$fiscalYearStart = sprintf('%s-%s', date('Y'), (string) $fiscalYearStartStr);
$tjOptionalFields = app('preferences')->get('transaction_journal_optional_fields', [])->data;
$availableDarkModes = config('firefly.available_dark_modes');
// notifications settings
$slackUrl = app('preferences')->getEncrypted('slack_webhook_url', '')->data;
$pushoverAppToken = app('preferences')->getEncrypted('pushover_app_token', '')->data;
$pushoverUserToken = app('preferences')->getEncrypted('pushover_user_token', '')->data;
$ntfyServer = app('preferences')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data;
$ntfyTopic = app('preferences')->getEncrypted('ntfy_topic', '')->data;
$ntfyAuth = app('preferences')->get('ntfy_auth', false)->data;
$ntfyUser = app('preferences')->getEncrypted('ntfy_user', '')->data;
$ntfyPass = app('preferences')->getEncrypted('ntfy_pass', '')->data;
$slackUrl = app('preferences')->getEncrypted('slack_webhook_url', '')->data;
$pushoverAppToken = app('preferences')->getEncrypted('pushover_app_token', '')->data;
$pushoverUserToken = app('preferences')->getEncrypted('pushover_user_token', '')->data;
$ntfyServer = app('preferences')->getEncrypted('ntfy_server', 'https://ntfy.sh')->data;
$ntfyTopic = app('preferences')->getEncrypted('ntfy_topic', '')->data;
$ntfyAuth = app('preferences')->get('ntfy_auth', false)->data;
$ntfyUser = app('preferences')->getEncrypted('ntfy_user', '')->data;
$ntfyPass = app('preferences')->getEncrypted('ntfy_pass', '')->data;
$channels = config('notifications.channels');
$forcedAvailability = [];
// notification preferences (single value for each):
$notifications = [];
// notification preferences
$notifications = [];
foreach (config('notifications.notifications.user') as $key => $info) {
if ($info['enabled']) {
$notifications[$key]
= [
'enabled' => app('preferences')->get(sprintf('notification_%s', $key), true)->data,
'configurable' => $info['configurable'],
];
'enabled' => app('preferences')->get(sprintf('notification_%s', $key), true)->data,
'configurable' => $info['configurable'],
];
}
}
// loop all channels to see if they are available.
foreach ($channels as $channel => $info) {
$forcedAvailability[$channel] = true;
}
$forcedAvailability['ntfy'] = '' !== $ntfyTopic;
$forcedAvailability['pushover'] = '' !== $pushoverAppToken && '' !== $pushoverUserToken;
ksort($languages);
@ -141,7 +148,7 @@ class PreferencesController extends Controller
app('log')->error($e->getMessage());
$locales = [];
}
$locales = ['equal' => (string) trans('firefly.equal_to_language')] + $locales;
$locales = ['equal' => (string) trans('firefly.equal_to_language')] + $locales;
// an important fallback is that the frontPageAccount array gets refilled automatically
// when it turns up empty.
if (0 === count($frontpageAccounts)) {
@ -151,7 +158,7 @@ class PreferencesController extends Controller
// for the demo user, the notification settings are automatically emptied.
// this isn't really secure, but it means that the demo site has semi-secret notification settings.
if (auth()->user()->hasRole('demo')) {
$slackUrl = '';
$slackUrl = '';
$pushoverAppToken = '';
$pushoverUserToken = '';
$ntfyServer = '';
@ -161,9 +168,32 @@ class PreferencesController extends Controller
$ntfyPass = '';
}
return view('preferences.index', compact('language', 'pushoverAppToken', 'pushoverUserToken',
'ntfyServer', 'ntfyTopic', 'ntfyAuth', 'ntfyUser', 'ntfyPass',
'groupedAccounts', 'isDocker', 'frontpageAccounts', 'languages', 'darkMode', 'availableDarkModes', 'notifications', 'slackUrl', 'locales', 'locale', 'tjOptionalFields', 'viewRange', 'customFiscalYear', 'listPageSize', 'fiscalYearStart'));
return view('preferences.index', compact(
'language',
'pushoverAppToken',
'pushoverUserToken',
'ntfyServer',
'ntfyTopic',
'ntfyAuth',
'ntfyUser',
'forcedAvailability',
'ntfyPass',
'groupedAccounts',
'isDocker',
'frontpageAccounts',
'languages',
'darkMode',
'availableDarkModes',
'notifications',
'slackUrl',
'locales',
'locale',
'tjOptionalFields',
'viewRange',
'customFiscalYear',
'listPageSize',
'fiscalYearStart'
));
}
/**
@ -188,8 +218,9 @@ class PreferencesController extends Controller
}
// extract notifications:
$all = $request->all();
die('fix the reference to the available notifications.');
$all = $request->all();
exit('fix the reference to the available notifications.');
foreach (config('firefly.available_notifications') as $option) {
$key = sprintf('notification_%s', $option);
if (array_key_exists($key, $all)) {
@ -219,8 +250,8 @@ class PreferencesController extends Controller
}
// custom fiscal year
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
$string = strtotime((string) $request->get('fiscalYearStart'));
$customFiscalYear = 1 === (int) $request->get('customFiscalYear');
$string = strtotime((string) $request->get('fiscalYearStart'));
if (false !== $string) {
$fiscalYearStart = date('m-d', $string);
app('preferences')->set('customFiscalYear', $customFiscalYear);
@ -229,15 +260,15 @@ class PreferencesController extends Controller
// save page size:
app('preferences')->set('listPageSize', 50);
$listPageSize = (int) $request->get('listPageSize');
$listPageSize = (int) $request->get('listPageSize');
if ($listPageSize > 0 && $listPageSize < 1337) {
app('preferences')->set('listPageSize', $listPageSize);
}
// language:
/** @var Preference $currentLang */
$currentLang = app('preferences')->get('language', 'en_US');
$lang = $request->get('language');
$currentLang = app('preferences')->get('language', 'en_US');
$lang = $request->get('language');
if (array_key_exists($lang, config('firefly.languages'))) {
app('preferences')->set('language', $lang);
}
@ -254,8 +285,8 @@ class PreferencesController extends Controller
}
// optional fields for transactions:
$setOptions = $request->get('tj') ?? [];
$optionalTj = [
$setOptions = $request->get('tj') ?? [];
$optionalTj = [
'interest_date' => array_key_exists('interest_date', $setOptions),
'book_date' => array_key_exists('book_date', $setOptions),
'process_date' => array_key_exists('process_date', $setOptions),
@ -272,7 +303,7 @@ class PreferencesController extends Controller
app('preferences')->set('transaction_journal_optional_fields', $optionalTj);
// dark mode
$darkMode = $request->get('darkMode') ?? 'browser';
$darkMode = $request->get('darkMode') ?? 'browser';
if (in_array($darkMode, config('firefly.available_dark_modes'), true)) {
app('preferences')->set('darkMode', $darkMode);
}

View File

@ -1,4 +1,5 @@
<?php
/*
* NotificationRequest.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -35,24 +36,25 @@ class NotificationRequest extends FormRequest
public function getAll(): array
{
$return = [];
$return = [];
foreach (config('notifications.notifications.owner') as $key => $info) {
$value = false;
$value = false;
if ($this->has(sprintf('notification_%s', $key))) {
$value = true;
}
$return[$key] = $value;
}
$return['slack_webhook_url'] = $this->convertString('slack_webhook_url');
$return['slack_webhook_url'] = $this->convertString('slack_webhook_url');
$return['pushover_app_token'] = $this->convertString('pushover_app_token');
$return['pushover_user_token'] = $this->convertString('pushover_user_token');
$return['ntfy_server'] = $this->convertString('ntfy_server');
$return['ntfy_topic'] = $this->convertString('ntfy_topic');
$return['ntfy_auth'] = $this->convertBoolean($this->get('ntfy_auth'));
$return['ntfy_user'] = $this->convertString('ntfy_user');
$return['ntfy_pass'] = $this->convertString('ntfy_pass');
$return['ntfy_server'] = $this->convertString('ntfy_server');
$return['ntfy_topic'] = $this->convertString('ntfy_topic');
$return['ntfy_auth'] = $this->convertBoolean($this->get('ntfy_auth'));
$return['ntfy_user'] = $this->convertString('ntfy_user');
$return['ntfy_pass'] = $this->convertString('ntfy_pass');
return $return;
}
@ -63,14 +65,14 @@ class NotificationRequest extends FormRequest
{
$rules = [
'slack_webhook_url' => ['nullable', 'url', 'min:1', new IsValidSlackOrDiscordUrl()],
'ntfy_server' => ['nullable', 'url', 'min:1'],
'ntfy_user' => ['required_with:ntfy_pass,ntfy_auth', 'nullable', 'string', 'min:1'],
'ntfy_pass' => ['required_with:ntfy_user,ntfy_auth', 'nullable', 'string', 'min:1'],
'ntfy_server' => ['nullable', 'url', 'min:1'],
'ntfy_user' => ['required_with:ntfy_pass,ntfy_auth', 'nullable', 'string', 'min:1'],
'ntfy_pass' => ['required_with:ntfy_user,ntfy_auth', 'nullable', 'string', 'min:1'],
];
foreach (config('notifications.notifications.owner') as $key => $info) {
$rules[sprintf('notification_%s', $key)] = 'in:0,1';
}
return $rules;
}
}

View File

@ -59,7 +59,7 @@ class PiggyBankStoreRequest extends FormRequest
$accounts = [];
}
foreach ($accounts as $item) {
$data['accounts'][] = ['account_id' => (int) ($item)];
$data['accounts'][] = ['account_id' => (int) $item];
}
return $data;
@ -105,7 +105,7 @@ class PiggyBankStoreRequest extends FormRequest
if ($accountCurrency->id !== $currency->id && 'true' !== $isMultiCurrency) {
$validator->errors()->add('accounts', trans('validation.invalid_account_currency'));
}
$type = $account->accountType->type;
$type = $account->accountType->type;
if (!in_array($type, $types, true)) {
$validator->errors()->add('accounts', trans('validation.invalid_account_type'));
}
@ -127,6 +127,7 @@ class PiggyBankStoreRequest extends FormRequest
if (null === $currency) {
return app('amount')->getDefaultCurrency();
}
return $currency;
}
}

View File

@ -44,18 +44,18 @@ class PiggyBank extends Model
protected $casts
= [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'deleted_at' => 'datetime',
'start_date' => 'date',
'target_date' => 'date',
'order' => 'int',
'active' => 'boolean',
'encrypted' => 'boolean',
'order' => 'int',
'active' => 'boolean',
'encrypted' => 'boolean',
'target_amount' => 'string',
];
protected $fillable = ['name', 'account_id', 'order', 'target_amount', 'start_date', 'start_date_tz', 'target_date', 'target_date_tz', 'active','transaction_currency_id'];
protected $fillable = ['name', 'account_id', 'order', 'target_amount', 'start_date', 'start_date_tz', 'target_date', 'target_date_tz', 'active', 'transaction_currency_id'];
/**
* Route binder. Converts the key in the URL to the specified object (or throw 404).
@ -67,9 +67,10 @@ class PiggyBank extends Model
if (auth()->check()) {
$piggyBankId = (int)$value;
$piggyBank = self::where('piggy_banks.id', $piggyBankId)
->leftJoin('account_piggy_bank','account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*']);
->where('accounts.user_id', auth()->user()->id)->first(['piggy_banks.*'])
;
if (null !== $piggyBank) {
return $piggyBank;
}

View File

@ -40,11 +40,11 @@ class PiggyBankRepetition extends Model
protected $casts
= [
'created_at' => 'datetime',
'updated_at' => 'datetime',
'created_at' => 'datetime',
'updated_at' => 'datetime',
'start_date' => SeparateTimezoneCaster::class,
'target_date' => SeparateTimezoneCaster::class,
'virtual_balance' => 'string',
'virtual_balance' => 'string',
];
protected $fillable = ['piggy_bank_id', 'start_date', 'start_date_tz', 'target_date', 'target_date_tz', 'current_amount'];

View File

@ -38,7 +38,6 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Log;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**

View File

@ -1,4 +1,5 @@
<?php
/*
* UnknownUserLoginAttempt.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -64,15 +65,14 @@ class UnknownUserLoginAttempt extends Notification
/**
* Get the mail representation of the notification.
*
* @return MailMessage
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/
public function toMail(OwnerNotifiable $notifiable): MailMessage
{
return new MailMessage()
->markdown('emails.owner.unknown-user', ['address' => $this->address])
->subject((string) trans('email.unknown_user_subject'));
->subject((string) trans('email.unknown_user_subject'))
;
}
/**
@ -90,7 +90,8 @@ class UnknownUserLoginAttempt extends Notification
public function toPushover(OwnerNotifiable $notifiable): PushoverMessage
{
return PushoverMessage::create((string) trans('email.unknown_user_message', ['address' => $this->address]))
->title((string) trans('email.unknown_user_subject'));
->title((string) trans('email.unknown_user_subject'))
;
}
public function toNtfy(OwnerNotifiable $notifiable): Message
@ -108,7 +109,7 @@ class UnknownUserLoginAttempt extends Notification
config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]);
}
$message = new Message();
$message = new Message();
$message->topic($settings['ntfy_topic']);
$message->title((string) trans('email.unknown_user_subject'));
$message->body((string) trans('email.unknown_user_message', ['address' => $this->address]));

View File

@ -83,7 +83,8 @@ class UserInvitation extends Notification
{
return (new MailMessage())
->markdown('emails.invitation-created', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email])
->subject((string) trans('email.invitation_created_subject'));
->subject((string) trans('email.invitation_created_subject'))
;
}
/**
@ -107,7 +108,8 @@ class UserInvitation extends Notification
Log::debug('Now in toPushover() for UserInvitation');
return PushoverMessage::create((string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email]))
->title((string) trans('email.invitation_created_subject'));
->title((string) trans('email.invitation_created_subject'))
;
}
public function toNtfy(OwnerNotifiable $notifiable): Message
@ -126,7 +128,7 @@ class UserInvitation extends Notification
config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]);
}
$message = new Message();
$message = new Message();
$message->topic($settings['ntfy_topic']);
$message->title((string) trans('email.invitation_created_subject'));
$message->body((string) trans('email.invitation_created_body', ['email' => $this->invitee->user->email, 'invitee' => $this->invitee->email]));

View File

@ -51,7 +51,7 @@ class UserRegistration extends Notification
*/
public function __construct(OwnerNotifiable $owner, User $user)
{
$this->user = $user;
$this->user = $user;
$this->owner = $owner;
}
@ -79,7 +79,8 @@ class UserRegistration extends Notification
{
return (new MailMessage())
->markdown('emails.registered-admin', ['email' => $this->user->email, 'id' => $this->user->id])
->subject((string) trans('email.registered_subject_admin'));
->subject((string) trans('email.registered_subject_admin'))
;
}
/**
@ -101,7 +102,8 @@ class UserRegistration extends Notification
Log::debug('Now in toPushover() for UserRegistration');
return PushoverMessage::create((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email]))
->title((string) trans('email.registered_subject_admin'));
->title((string) trans('email.registered_subject_admin'))
;
}
public function toNtfy(OwnerNotifiable $notifiable): Message
@ -120,10 +122,10 @@ class UserRegistration extends Notification
config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]);
}
$message = new Message();
$message = new Message();
$message->topic($settings['ntfy_topic']);
$message->title((string) trans('email.registered_subject_admin'));
$message->body((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email]) );
$message->body((string) trans('email.admin_new_user_registered', ['email' => $this->user->email, 'invitee' => $this->user->email]));
return $message;
}

View File

@ -27,7 +27,6 @@ namespace FireflyIII\Notifications\Admin;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsAvailableChannels;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\Support\Notifications\UrlValidator;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
@ -108,7 +107,8 @@ class VersionCheckResult extends Notification
Log::debug('Now in toPushover() for VersionCheckResult');
return PushoverMessage::create($this->message)
->title((string) trans('email.new_version_email_subject'));
->title((string) trans('email.new_version_email_subject'))
;
}
public function toNtfy(OwnerNotifiable $notifiable): Message
@ -127,7 +127,7 @@ class VersionCheckResult extends Notification
config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]);
}
$message = new Message();
$message = new Message();
$message->topic($settings['ntfy_topic']);
$message->title((string) trans('email.new_version_email_subject'));
$message->body($this->message);

View File

@ -1,4 +1,5 @@
<?php
/*
* AdminNotifiable.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -30,13 +31,13 @@ use NotificationChannels\Pushover\PushoverReceiver;
class OwnerNotifiable
{
public function routeNotificationForSlack(): string
{
$res = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
if (is_array($res)) {
$res = '';
}
return (string) $res;
}
@ -45,8 +46,10 @@ class OwnerNotifiable
Log::debug('Return settings for routeNotificationForPushover');
$pushoverAppToken = (string) app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data;
$pushoverUserToken = (string) app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data;
return PushoverReceiver::withUserKey($pushoverUserToken)
->withApplicationToken($pushoverAppToken);
->withApplicationToken($pushoverAppToken)
;
}
/**
@ -59,9 +62,10 @@ class OwnerNotifiable
*/
public function routeNotificationFor($driver, $notification = null)
{
$method = 'routeNotificationFor' . Str::studly($driver);
$method = 'routeNotificationFor'.Str::studly($driver);
if (method_exists($this, $method)) {
Log::debug(sprintf('Redirect for settings to "%s".', $method));
return $this->{$method}($notification); // @phpstan-ignore-line
}
Log::debug(sprintf('No method "%s" found, return generic settings.', $method));

View File

@ -1,4 +1,5 @@
<?php
/*
* ReturnsAvailableChannels.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -46,8 +47,8 @@ class ReturnsAvailableChannels
private static function returnOwnerChannels(): array
{
$channels = ['mail'];
$slackUrl = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
$channels = ['mail'];
$slackUrl = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
if (UrlValidator::isValidWebhookURL($slackUrl)) {
$channels[] = 'slack';
}
@ -73,8 +74,8 @@ class ReturnsAvailableChannels
}
Log::debug(sprintf('Final channel set in ReturnsAvailableChannels: %s ', implode(', ', $channels)));
// only the owner can get notifications over
return $channels;
}
}

View File

@ -1,4 +1,5 @@
<?php
/*
* ReturnsSettings.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -34,6 +35,7 @@ class ReturnsSettings
if ('ntfy' === $channel) {
return self::getNtfySettings($type, $user);
}
throw new FireflyException(sprintf('Cannot handle channel "%s"', $channel));
}
@ -54,7 +56,7 @@ class ReturnsSettings
$settings['ntfy_user'] = FireflyConfig::getEncrypted('ntfy_user', '')->data;
$settings['ntfy_pass'] = FireflyConfig::getEncrypted('ntfy_pass', '')->data;
}
return $settings;
}
}

View File

@ -49,8 +49,6 @@ class TestNotificationEmail extends Notification
/**
* Get the array representation of the notification.
*
* @param OwnerNotifiable $notifiable
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return array
@ -73,9 +71,11 @@ class TestNotificationEmail extends Notification
public function toMail(OwnerNotifiable $notifiable)
{
$address = (string) config('firefly.site_owner');
return (new MailMessage())
->markdown('emails.admin-test', ['email' => $address])
->subject((string) trans('email.admin_test_subject'));
->subject((string) trans('email.admin_test_subject'))
;
}
/**

View File

@ -26,13 +26,12 @@ namespace FireflyIII\Notifications\Test;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\Notifications\ReturnsSettings;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Notification;
use Ntfy\Message;
use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel;
//use Illuminate\Notifications\Slack\SlackMessage;
// use Illuminate\Notifications\Slack\SlackMessage;
/**
* Class TestNotification
@ -66,7 +65,6 @@ class TestNotificationNtfy extends Notification
];
}
public function toNtfy(OwnerNotifiable $notifiable): Message
{
$settings = ReturnsSettings::getSettings('ntfy', 'owner', null);
@ -82,7 +80,7 @@ class TestNotificationNtfy extends Notification
config(['ntfy-notification-channel.authentication.password' => $settings['ntfy_pass']]);
}
$message = new Message();
$message = new Message();
$message->topic($settings['ntfy_topic']);
$message->title((string) trans('email.admin_test_subject'));
$message->body((string) trans('email.admin_test_message', ['channel' => 'ntfy']));

View File

@ -25,17 +25,13 @@ declare(strict_types=1);
namespace FireflyIII\Notifications\Test;
use FireflyIII\Notifications\Notifiables\OwnerNotifiable;
use FireflyIII\User;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Log;
use NotificationChannels\Pushover\PushoverChannel;
use NotificationChannels\Pushover\PushoverMessage;
use Ntfy\Message;
use Wijourdil\NtfyNotificationChannel\Channels\NtfyChannel;
//use Illuminate\Notifications\Slack\SlackMessage;
// use Illuminate\Notifications\Slack\SlackMessage;
/**
* Class TestNotification
@ -57,8 +53,6 @@ class TestNotificationPushover extends Notification
/**
* Get the array representation of the notification.
*
* @param OwnerNotifiable $notifiable
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return array
@ -69,16 +63,15 @@ class TestNotificationPushover extends Notification
];
}
public function toPushover(OwnerNotifiable $notifiable): PushoverMessage
{
Log::debug('Now in toPushover()');
return PushoverMessage::create((string)trans('email.admin_test_message', ['channel' => 'Pushover']))
->title((string)trans('email.admin_test_subject'));
->title((string)trans('email.admin_test_subject'))
;
}
/**
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*/

View File

@ -29,7 +29,7 @@ use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
//use Illuminate\Notifications\Slack\SlackMessage;
// use Illuminate\Notifications\Slack\SlackMessage;
/**
* Class TestNotification
@ -51,8 +51,6 @@ class TestNotificationSlack extends Notification
/**
* Get the array representation of the notification.
*
* @param OwnerNotifiable $notifiable
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @return array
@ -66,15 +64,12 @@ class TestNotificationSlack extends Notification
/**
* Get the Slack representation of the notification.
*
* @param OwnerNotifiable $notifiable
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
*/
public function toSlack(OwnerNotifiable $notifiable)
{
return new SlackMessage()->content((string) trans('email.admin_test_subject'));
//return new SlackMessage()->text((string) trans('email.admin_test_subject'))->to($url);
// return new SlackMessage()->text((string) trans('email.admin_test_subject'))->to($url);
}
/**
@ -82,8 +77,6 @@ class TestNotificationSlack extends Notification
*
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
*
* @param OwnerNotifiable $notifiable
*
* @return array
*/
public function via(OwnerNotifiable $notifiable)

View File

@ -25,7 +25,6 @@ namespace FireflyIII\Providers;
use FireflyIII\Events\ActuallyLoggedIn;
use FireflyIII\Events\Admin\InvitationCreated;
use FireflyIII\Events\AdminRequestedTestMessage;
use FireflyIII\Events\DestroyedTransactionGroup;
use FireflyIII\Events\DetectedNewIPAddress;
use FireflyIII\Events\Model\BudgetLimit\Created;
@ -137,7 +136,7 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail',
],
// admin related
TestNotificationChannel::class => [
TestNotificationChannel::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendTestNotification',
],
NewVersionAvailable::class => [
@ -147,7 +146,7 @@ class EventServiceProvider extends ServiceProvider
'FireflyIII\Handlers\Events\AdminEventHandler@sendInvitationNotification',
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationInvite',
],
UnknownUserAttemptedLogin::class => [
UnknownUserAttemptedLogin::class => [
'FireflyIII\Handlers\Events\AdminEventHandler@sendLoginAttemptNotification',
],

View File

@ -50,10 +50,10 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
*/
public function budgeted(Carbon $start, Carbon $end, TransactionCurrency $currency, ?Collection $budgets = null): string
{
$query = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
$query = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
// same complex where query as below.
->where(
->where(
static function (Builder $q5) use ($start, $end): void {
$q5->where(
static function (Builder $q1) use ($start, $end): void {
@ -63,27 +63,30 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d'));
}
)
->orWhere(
static function (Builder $q3) use ($start, $end): void {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d'));
}
);
->orWhere(
static function (Builder $q3) use ($start, $end): void {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d'));
}
)
;
}
)
->orWhere(
static function (Builder $q4) use ($start, $end): void {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d'));
}
);
->orWhere(
static function (Builder $q4) use ($start, $end): void {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d'));
}
)
;
}
)
->where('budget_limits.transaction_currency_id', $currency->id)
->whereNull('budgets.deleted_at')
->where('budgets.active', true)
->where('budgets.user_id', $this->user->id);
->where('budget_limits.transaction_currency_id', $currency->id)
->whereNull('budgets.deleted_at')
->where('budgets.active', true)
->where('budgets.user_id', $this->user->id)
;
if (null !== $budgets && $budgets->count() > 0) {
$query->whereIn('budget_limits.budget_id', $budgets->pluck('id')->toArray());
}
@ -135,17 +138,19 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
// both are NULL:
if (null === $start && null === $end) {
return BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->with(['budget'])
->where('budgets.user_id', $this->user->id)
->whereNull('budgets.deleted_at')
->get(['budget_limits.*']);
->with(['budget'])
->where('budgets.user_id', $this->user->id)
->whereNull('budgets.deleted_at')
->get(['budget_limits.*'])
;
}
// one of the two is NULL.
if (null === $start xor null === $end) {
$query = BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->with(['budget'])
->whereNull('budgets.deleted_at')
->where('budgets.user_id', $this->user->id);
->with(['budget'])
->whereNull('budgets.deleted_at')
->where('budgets.user_id', $this->user->id)
;
if (null !== $end) {
// end date must be before $end.
$query->where('end_date', '<=', $end->format('Y-m-d 00:00:00'));
@ -160,36 +165,39 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
// neither are NULL:
return BudgetLimit::leftJoin('budgets', 'budgets.id', '=', 'budget_limits.budget_id')
->with(['budget'])
->where('budgets.user_id', $this->user->id)
->whereNull('budgets.deleted_at')
->where(
static function (Builder $q5) use ($start, $end): void {
$q5->where(
static function (Builder $q1) use ($start, $end): void {
$q1->where(
static function (Builder $q2) use ($start, $end): void {
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d'));
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d'));
}
)
->orWhere(
static function (Builder $q3) use ($start, $end): void {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d'));
}
);
}
)
->orWhere(
static function (Builder $q4) use ($start, $end): void {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d'));
}
);
}
)->get(['budget_limits.*']);
->with(['budget'])
->where('budgets.user_id', $this->user->id)
->whereNull('budgets.deleted_at')
->where(
static function (Builder $q5) use ($start, $end): void {
$q5->where(
static function (Builder $q1) use ($start, $end): void {
$q1->where(
static function (Builder $q2) use ($start, $end): void {
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d'));
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d'));
}
)
->orWhere(
static function (Builder $q3) use ($start, $end): void {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d'));
}
)
;
}
)
->orWhere(
static function (Builder $q4) use ($start, $end): void {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d'));
}
)
;
}
)->get(['budget_limits.*'])
;
}
public function getBudgetLimits(Budget $budget, ?Carbon $start = null, ?Carbon $end = null): Collection
@ -214,38 +222,41 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
// when both dates are set:
return $budget->budgetlimits()
->where(
static function (Builder $q5) use ($start, $end): void { // @phpstan-ignore-line
$q5->where(
static function (Builder $q1) use ($start, $end): void {
// budget limit ends within period
$q1->where(
static function (Builder $q2) use ($start, $end): void {
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 23:59:59'));
}
)
// budget limit start within period
->orWhere(
static function (Builder $q3) use ($start, $end): void {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59'));
}
);
}
)
->orWhere(
static function (Builder $q4) use ($start, $end): void {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 23:59:59'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
}
);
}
)->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*']);
->where(
static function (Builder $q5) use ($start, $end): void { // @phpstan-ignore-line
$q5->where(
static function (Builder $q1) use ($start, $end): void {
// budget limit ends within period
$q1->where(
static function (Builder $q2) use ($start, $end): void {
$q2->where('budget_limits.end_date', '>=', $start->format('Y-m-d 00:00:00'));
$q2->where('budget_limits.end_date', '<=', $end->format('Y-m-d 23:59:59'));
}
)
// budget limit start within period
->orWhere(
static function (Builder $q3) use ($start, $end): void {
$q3->where('budget_limits.start_date', '>=', $start->format('Y-m-d 00:00:00'));
$q3->where('budget_limits.start_date', '<=', $end->format('Y-m-d 23:59:59'));
}
)
;
}
)
->orWhere(
static function (Builder $q4) use ($start, $end): void {
// or start is before start AND end is after end.
$q4->where('budget_limits.start_date', '<=', $start->format('Y-m-d 23:59:59'));
$q4->where('budget_limits.end_date', '>=', $end->format('Y-m-d 00:00:00'));
}
)
;
}
)->orderBy('budget_limits.start_date', 'DESC')->get(['budget_limits.*'])
;
}
public function setUser(null | Authenticatable | User $user): void
public function setUser(null|Authenticatable|User $user): void
{
if ($user instanceof User) {
$this->user = $user;
@ -259,33 +270,34 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
{
// if no currency has been provided, use the user's default currency:
/** @var TransactionCurrencyFactory $factory */
$factory = app(TransactionCurrencyFactory::class);
$currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null);
$factory = app(TransactionCurrencyFactory::class);
$currency = $factory->find($data['currency_id'] ?? null, $data['currency_code'] ?? null);
if (null === $currency) {
$currency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup);
}
$currency->enabled = true;
$currency->enabled = true;
$currency->save();
// find the budget:
$budget = $this->user->budgets()->find((int) $data['budget_id']);
$budget = $this->user->budgets()->find((int) $data['budget_id']);
if (null === $budget) {
throw new FireflyException('200004: Budget does not exist.');
}
// find limit with same date range and currency.
$limit = $budget->budgetlimits()
->where('budget_limits.start_date', $data['start_date']->format('Y-m-d'))
->where('budget_limits.end_date', $data['end_date']->format('Y-m-d'))
->where('budget_limits.transaction_currency_id', $currency->id)
->first(['budget_limits.*']);
$limit = $budget->budgetlimits()
->where('budget_limits.start_date', $data['start_date']->format('Y-m-d'))
->where('budget_limits.end_date', $data['end_date']->format('Y-m-d'))
->where('budget_limits.transaction_currency_id', $currency->id)
->first(['budget_limits.*'])
;
if (null !== $limit) {
throw new FireflyException('200027: Budget limit already exists.');
}
app('log')->debug('No existing budget limit, create a new one');
// or create one and return it.
$limit = new BudgetLimit();
$limit = new BudgetLimit();
$limit->budget()->associate($budget);
$limit->start_date = $data['start_date']->format('Y-m-d');
$limit->end_date = $data['end_date']->format('Y-m-d');
@ -293,7 +305,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
$limit->transaction_currency_id = $currency->id;
$limit->save();
$noteText = (string) ($data['notes'] ?? '');
$noteText = (string) ($data['notes'] ?? '');
if ('' !== $noteText) {
$this->setNoteText($limit, $noteText);
}
@ -303,13 +315,13 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
return $limit;
}
public function find(Budget $budget, TransactionCurrency $currency, Carbon $start, Carbon $end): ?BudgetLimit
{
return $budget->budgetlimits()
->where('transaction_currency_id', $currency->id)
->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->first();
->where('transaction_currency_id', $currency->id)
->where('start_date', $start->format('Y-m-d'))
->where('end_date', $end->format('Y-m-d'))->first()
;
}
/**
@ -317,8 +329,8 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
*/
public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit
{
$budgetLimit->amount = array_key_exists('amount', $data) ? $data['amount'] : $budgetLimit->amount;
$budgetLimit->budget_id = array_key_exists('budget_id', $data) ? $data['budget_id'] : $budgetLimit->budget_id;
$budgetLimit->amount = array_key_exists('amount', $data) ? $data['amount'] : $budgetLimit->amount;
$budgetLimit->budget_id = array_key_exists('budget_id', $data) ? $data['budget_id'] : $budgetLimit->budget_id;
if (array_key_exists('start', $data)) {
$budgetLimit->start_date = $data['start']->startOfDay();
@ -330,7 +342,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
}
// if no currency has been provided, use the user's default currency:
$currency = null;
$currency = null;
// update if relevant:
if (array_key_exists('currency_id', $data) || array_key_exists('currency_code', $data)) {
@ -342,14 +354,14 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
if (null === $currency) {
$currency = $budgetLimit->transactionCurrency ?? app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup);
}
$currency->enabled = true;
$currency->enabled = true;
$currency->save();
$budgetLimit->transaction_currency_id = $currency->id;
$budgetLimit->save();
// update notes if they exist.
if(array_key_exists('notes', $data)) {
if (array_key_exists('notes', $data)) {
$this->setNoteText($budgetLimit, (string)$data['notes']);
}
@ -359,26 +371,29 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
public function updateLimitAmount(Budget $budget, Carbon $start, Carbon $end, string $amount): ?BudgetLimit
{
// count the limits:
$limits = $budget->budgetlimits()
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
->count('budget_limits.*');
$limits = $budget->budgetlimits()
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
->count('budget_limits.*')
;
app('log')->debug(sprintf('Found %d budget limits.', $limits));
// there might be a budget limit for these dates:
/** @var null|BudgetLimit $limit */
$limit = $budget->budgetlimits()
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
->first(['budget_limits.*']);
$limit = $budget->budgetlimits()
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
->first(['budget_limits.*'])
;
// if more than 1 limit found, delete the others:
if ($limits > 1 && null !== $limit) {
app('log')->debug(sprintf('Found more than 1, delete all except #%d', $limit->id));
$budget->budgetlimits()
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
->where('budget_limits.id', '!=', $limit->id)->delete();
->where('budget_limits.start_date', $start->format('Y-m-d 00:00:00'))
->where('budget_limits.end_date', $end->format('Y-m-d 00:00:00'))
->where('budget_limits.id', '!=', $limit->id)->delete()
;
}
// delete if amount is zero.
@ -400,7 +415,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
}
app('log')->debug('No existing budget limit, create a new one');
// or create one and return it.
$limit = new BudgetLimit();
$limit = new BudgetLimit();
$limit->budget()->associate($budget);
$limit->start_date = $start->startOfDay();
$limit->start_date_tz = $start->format('e');
@ -413,12 +428,14 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
return $limit;
}
#[\Override] public function getNoteText(BudgetLimit $budgetLimit): string
#[\Override]
public function getNoteText(BudgetLimit $budgetLimit): string
{
return (string) $budgetLimit->notes()->first()?->text;
}
#[\Override] public function setNoteText(BudgetLimit $budgetLimit, string $text): void
#[\Override]
public function setNoteText(BudgetLimit $budgetLimit, string $text): void
{
$dbNote = $budgetLimit->notes()->first();
if ('' !== $text) {

View File

@ -49,6 +49,7 @@ interface BudgetLimitRepositoryInterface
public function destroyAll(): void;
public function getNoteText(BudgetLimit $budgetLimit): string;
public function setNoteText(BudgetLimit $budgetLimit, string $text): void;
/**

View File

@ -106,7 +106,8 @@ class CurrencyRepository implements CurrencyRepositoryInterface
}
}
#[\Override] public function find(int $currencyId): ?TransactionCurrency
#[\Override]
public function find(int $currencyId): ?TransactionCurrency
{
return TransactionCurrency::find($currencyId);
}

View File

@ -30,11 +30,9 @@ use FireflyIII\Factory\PiggyBankFactory;
use FireflyIII\Models\Note;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Repositories\ObjectGroup\CreatesObjectGroups;
use FireflyIII\Support\Facades\Amount;
use Illuminate\Database\QueryException;
/**
* Trait ModifiesPiggyBanks
@ -59,7 +57,7 @@ trait ModifiesPiggyBanks
public function removeAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool
{
$repetition = $this->getRepetition($piggyBank);
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return false;
}
@ -74,11 +72,11 @@ trait ModifiesPiggyBanks
public function addAmount(PiggyBank $piggyBank, string $amount, ?TransactionJournal $journal = null): bool
{
$repetition = $this->getRepetition($piggyBank);
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return false;
}
$currentAmount = $repetition->current_amount ?? '0';
$currentAmount = $repetition->current_amount ?? '0';
$repetition->current_amount = bcadd($currentAmount, $amount);
$repetition->save();
@ -143,15 +141,15 @@ trait ModifiesPiggyBanks
public function setCurrentAmount(PiggyBank $piggyBank, string $amount): PiggyBank
{
$repetition = $this->getRepetition($piggyBank);
$repetition = $this->getRepetition($piggyBank);
if (null === $repetition) {
return $piggyBank;
}
$max = $piggyBank->target_amount;
$max = $piggyBank->target_amount;
if (1 === bccomp($amount, $max) && 0 !== bccomp($piggyBank->target_amount, '0')) {
$amount = $max;
}
$difference = bcsub($amount, $repetition->current_amount);
$difference = bcsub($amount, $repetition->current_amount);
$repetition->current_amount = $amount;
$repetition->save();
@ -184,10 +182,10 @@ trait ModifiesPiggyBanks
{
$factory = new PiggyBankFactory();
$factory->user = $this->user;
return $factory->store($data);
}
public function setOrder(PiggyBank $piggyBank, int $newOrder): bool
{
$oldOrder = $piggyBank->order;
@ -220,7 +218,8 @@ trait ModifiesPiggyBanks
if ('' === $note) {
$dbNote = $piggyBank->notes()->first();
$dbNote?->delete();
return ;
return;
}
$dbNote = $piggyBank->notes()->first();
if (null === $dbNote) {
@ -249,7 +248,7 @@ trait ModifiesPiggyBanks
// remove money from the rep.
$repetition = $this->getRepetition($piggyBank);
if (null !== $repetition && $repetition->current_amount > $piggyBank->target_amount && 0 !== bccomp($piggyBank->target_amount, '0')) {
$difference = bcsub($piggyBank->target_amount, $repetition->current_amount);
$difference = bcsub($piggyBank->target_amount, $repetition->current_amount);
// an amount will be removed, create "negative" event:
event(new ChangedAmount($piggyBank, $difference, null, null));

View File

@ -95,7 +95,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function getAttachments(PiggyBank $piggyBank): Collection
{
$set = $piggyBank->attachments()->get();
$set = $piggyBank->attachments()->get();
/** @var \Storage $disk */
$disk = \Storage::disk('upload');
@ -122,12 +122,14 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
$amount = '' === $amount ? '0' : $amount;
$sum = bcadd($sum, $amount);
}
return $sum;
}
public function getRepetition(PiggyBank $piggyBank): ?PiggyBankRepetition
{
throw new FireflyException('[b] Piggy bank repetitions are EOL.');
return $piggyBank->piggyBankRepetitions()->first();
}
@ -146,15 +148,15 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
throw new FireflyException('[c] Piggy bank repetitions are EOL.');
app('log')->debug(sprintf('Now in getExactAmount(%d, %d, %d)', $piggyBank->id, $repetition->id, $journal->id));
$operator = null;
$currency = null;
$operator = null;
$currency = null;
/** @var JournalRepositoryInterface $journalRepost */
$journalRepost = app(JournalRepositoryInterface::class);
$journalRepost = app(JournalRepositoryInterface::class);
$journalRepost->setUser($this->user);
/** @var AccountRepositoryInterface $accountRepos */
$accountRepos = app(AccountRepositoryInterface::class);
$accountRepos = app(AccountRepositoryInterface::class);
$accountRepos->setUser($this->user);
$defaultCurrency = app('amount')->getDefaultCurrencyByUserGroup($this->user->userGroup);
@ -163,10 +165,10 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
app('log')->debug(sprintf('Piggy bank #%d currency is %s', $piggyBank->id, $piggyBankCurrency->code));
/** @var Transaction $source */
$source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first();
$source = $journal->transactions()->with(['account'])->where('amount', '<', 0)->first();
/** @var Transaction $destination */
$destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first();
$destination = $journal->transactions()->with(['account'])->where('amount', '>', 0)->first();
// matches source, which means amount will be removed from piggy:
if ($source->account_id === $piggyBank->account_id) {
@ -188,7 +190,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
}
// currency of the account + the piggy bank currency are almost the same.
// which amount from the transaction matches?
$amount = null;
$amount = null;
if ((int) $source->transaction_currency_id === $currency->id) {
app('log')->debug('Use normal amount');
$amount = app('steam')->{$operator}($source->amount); // @phpstan-ignore-line
@ -204,8 +206,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
}
app('log')->debug(sprintf('The currency is %s and the amount is %s', $currency->code, $amount));
$room = bcsub($piggyBank->target_amount, $repetition->current_amount);
$compare = bcmul($repetition->current_amount, '-1');
$room = bcsub($piggyBank->target_amount, $repetition->current_amount);
$compare = bcmul($repetition->current_amount, '-1');
if (0 === bccomp($piggyBank->target_amount, '0')) {
// amount is zero? then the "room" is positive amount of we wish to add or remove.
@ -237,14 +239,13 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
return (string) $amount;
}
public function setUser(null | Authenticatable | User $user): void
public function setUser(null|Authenticatable|User $user): void
{
if ($user instanceof User) {
$this->user = $user;
}
}
/**
* Return note for piggy bank.
*/
@ -263,12 +264,12 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
{
$currency = app('amount')->getDefaultCurrency();
$set = $this->getPiggyBanks();
$set = $this->getPiggyBanks();
/** @var PiggyBank $piggy */
foreach ($set as $piggy) {
$currentAmount = $this->getRepetition($piggy)->current_amount ?? '0';
$piggy->name = $piggy->name . ' (' . app('amount')->formatAnything($currency, $currentAmount, false) . ')';
$piggy->name = $piggy->name.' ('.app('amount')->formatAnything($currency, $currentAmount, false).')';
}
return $set;
@ -276,8 +277,7 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
public function getPiggyBanks(): Collection
{
return PiggyBank
::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
return PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('accounts.user_id', auth()->user()->id)
->with(
@ -286,7 +286,8 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
'objectGroups',
]
)
->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*']);
->orderBy('piggy_banks.order', 'ASC')->get(['piggy_banks.*'])
;
}
/**
@ -344,17 +345,20 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
$search->whereLike('piggy_banks.name', sprintf('%%%s%%', $query));
}
$search->orderBy('piggy_banks.order', 'ASC')
->orderBy('piggy_banks.name', 'ASC');
->orderBy('piggy_banks.name', 'ASC')
;
return $search->take($limit)->get();
}
#[\Override] public function purgeAll(): void
#[\Override]
public function purgeAll(): void
{
throw new FireflyException('TODO Not implemented');
}
#[\Override] public function resetOrder(): void
#[\Override]
public function resetOrder(): void
{
$factory = new PiggyBankFactory();
$factory->user = $this->user;

View File

@ -129,7 +129,7 @@ interface PiggyBankRepositoryInterface
*/
public function setOrder(PiggyBank $piggyBank, int $newOrder): bool;
public function setUser(null | Authenticatable | User $user): void;
public function setUser(null|Authenticatable|User $user): void;
/**
* Store new piggy bank.

View File

@ -18,11 +18,11 @@ class IsValidDiscordUrl implements ValidationRule
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
$value = (string)$value;
if('' === $value) {
if ('' === $value) {
return;
}
if(!str_starts_with($value, 'https://discord.com/api/webhooks/')) {
if (!str_starts_with($value, 'https://discord.com/api/webhooks/')) {
$fail('validation.active_url')->translate();
$message = sprintf('IsValidDiscordUrl: "%s" is not a discord URL.', substr($value, 0, 255));
Log::debug($message);

View File

@ -18,11 +18,11 @@ class IsValidSlackOrDiscordUrl implements ValidationRule
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
$value = (string)$value;
if('' === $value) {
if ('' === $value) {
return;
}
if(!str_starts_with($value, 'https://hooks.slack.com/services/') && !str_starts_with($value, 'https://discord.com/api/webhooks/')) {
if (!str_starts_with($value, 'https://hooks.slack.com/services/') && !str_starts_with($value, 'https://discord.com/api/webhooks/')) {
$fail('validation.active_url')->translate();
$message = sprintf('IsValidSlackUrl: "%s" is not a discord or slack URL.', substr($value, 0, 255));
Log::debug($message);

View File

@ -18,11 +18,11 @@ class IsValidSlackUrl implements ValidationRule
public function validate(string $attribute, mixed $value, \Closure $fail): void
{
$value = (string)$value;
if('' === $value) {
if ('' === $value) {
return;
}
if(!str_starts_with($value, 'https://hooks.slack.com/services/')) {
if (!str_starts_with($value, 'https://hooks.slack.com/services/')) {
$fail('validation.active_url')->translate();
$message = sprintf('IsValidSlackUrl: "%s" is not a slack URL.', substr($value, 0, 255));
Log::debug($message);

View File

@ -289,6 +289,7 @@ class ExpandedForm
return $html;
}
/**
* @throws FireflyException
*/
@ -299,7 +300,7 @@ class ExpandedForm
$classes = $this->getHolderClasses($name);
try {
$html = view('form.password', compact('classes', 'value','name', 'label', 'options'))->render();
$html = view('form.password', compact('classes', 'value', 'name', 'label', 'options'))->render();
} catch (\Throwable $e) {
app('log')->debug(sprintf('Could not render passwordWithValue(): %s', $e->getMessage()));
$html = 'Could not render passwordWithValue.';

View File

@ -37,7 +37,7 @@ class FireflyConfig
{
public function delete(string $name): void
{
$fullName = 'ff-config-' . $name;
$fullName = 'ff-config-'.$name;
if (\Cache::has($fullName)) {
\Cache::forget($fullName);
}
@ -57,14 +57,18 @@ class FireflyConfig
}
if ('' === $result->data) {
Log::warning(sprintf('Empty encrypted configuration value found: "%s"', $name));
return $result;
}
try {
$result->data = decrypt($result->data);
} catch (DecryptException $e) {
Log::error(sprintf('Could not decrypt configuration value "%s": %s', $name, $e->getMessage()));
return $result;
}
return $result;
}
@ -75,7 +79,7 @@ class FireflyConfig
*/
public function get(string $name, $default = null): ?Configuration
{
$fullName = 'ff-config-' . $name;
$fullName = 'ff-config-'.$name;
if (\Cache::has($fullName)) {
return \Cache::get($fullName);
}
@ -83,7 +87,7 @@ class FireflyConfig
try {
/** @var null|Configuration $config */
$config = Configuration::where('name', $name)->first(['id', 'name', 'data']);
} catch (\Exception | QueryException $e) {
} catch (\Exception|QueryException $e) {
throw new FireflyException(sprintf('Could not poll the database: %s', $e->getMessage()), 0, $e);
}
@ -106,8 +110,10 @@ class FireflyConfig
$encrypted = encrypt($value);
} catch (EncryptException $e) {
Log::error(sprintf('Could not encrypt configuration value "%s": %s', $name, $e->getMessage()));
throw new FireflyException(sprintf('Could not encrypt configuration value "%s". Cowardly refuse to continue.', $name));
}
return $this->set($name, $encrypted);
}
@ -129,13 +135,13 @@ class FireflyConfig
$item->name = $name;
$item->data = $value;
$item->save();
\Cache::forget('ff-config-' . $name);
\Cache::forget('ff-config-'.$name);
return $item;
}
$config->data = $value;
$config->save();
\Cache::forget('ff-config-' . $name);
\Cache::forget('ff-config-'.$name);
return $config;
}

View File

@ -155,8 +155,9 @@ class AccountForm
*/
public function assetLiabilityMultiAccountList(string $name, $value = null, ?array $options = null): string
{
$types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value,AccountTypeEnum::LOAN->value];
$types = [AccountTypeEnum::ASSET->value, AccountTypeEnum::DEFAULT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value];
$grouped = $this->getAccountsGrouped($types);
return $this->multiSelect($name, $grouped, $value, $options);
}

View File

@ -64,14 +64,18 @@ class Preferences
}
if ('' === $result->data) {
Log::warning(sprintf('Empty encrypted preference found: "%s"', $name));
return $result;
}
try {
$result->data = decrypt($result->data);
} catch (DecryptException $e) {
Log::error(sprintf('Could not decrypt preference "%s": %s', $name, $e->getMessage()));
return $result;
}
return $result;
}
@ -81,12 +85,13 @@ class Preferences
$encrypted = encrypt($value);
} catch (EncryptException $e) {
Log::error(sprintf('Could not encrypt preference "%s": %s', $name, $e->getMessage()));
throw new FireflyException(sprintf('Could not encrypt preference "%s". Cowardly refuse to continue.', $name));
}
return $this->set($name, $encrypted);
}
public function get(string $name, null|array|bool|int|string $default = null): ?Preference
{
/** @var null|User $user */

View File

@ -55,8 +55,8 @@ class BudgetLimitTransformer extends AbstractTransformer
*/
public function transform(BudgetLimit $budgetLimit): array
{
$repository = app(OperationsRepository::class);
$limitRepos = app(BudgetLimitRepositoryInterface::class);
$repository = app(OperationsRepository::class);
$limitRepos = app(BudgetLimitRepositoryInterface::class);
$repository->setUser($budgetLimit->budget->user);
$limitRepos->setUser($budgetLimit->budget->user);
$expenses = $repository->sumExpenses(
@ -82,7 +82,7 @@ class BudgetLimitTransformer extends AbstractTransformer
$currencySymbol = $currency->symbol;
$currencyDecimalPlaces = $currency->decimal_places;
}
$amount = app('steam')->bcround($amount, $currencyDecimalPlaces);
$amount = app('steam')->bcround($amount, $currencyDecimalPlaces);
return [
'id' => (string) $budgetLimit->id,
@ -103,7 +103,7 @@ class BudgetLimitTransformer extends AbstractTransformer
'links' => [
[
'rel' => 'self',
'uri' => '/budgets/limits/' . $budgetLimit->id,
'uri' => '/budgets/limits/'.$budgetLimit->id,
],
],
];

View File

@ -54,22 +54,22 @@ class PiggyBankTransformer extends AbstractTransformer
*/
public function transform(PiggyBank $piggyBank): array
{
$user = $piggyBank->accounts()->first()->user;
$user = $piggyBank->accounts()->first()->user;
// set up repositories
$this->accountRepos->setUser($user);
$this->piggyRepos->setUser($user);
// note
$notes = $this->piggyRepos->getNoteText($piggyBank);
$notes = '' === $notes ? null : $notes;
$notes = $this->piggyRepos->getNoteText($piggyBank);
$notes = '' === $notes ? null : $notes;
$objectGroupId = null;
$objectGroupOrder = null;
$objectGroupTitle = null;
/** @var null|ObjectGroup $objectGroup */
$objectGroup = $piggyBank->objectGroups->first();
$objectGroup = $piggyBank->objectGroups->first();
if (null !== $objectGroup) {
$objectGroupId = $objectGroup->id;
$objectGroupOrder = $objectGroup->order;
@ -77,14 +77,14 @@ class PiggyBankTransformer extends AbstractTransformer
}
// get currently saved amount:
$currency = $piggyBank->transactionCurrency;
$currentAmount = app('steam')->bcround($this->piggyRepos->getCurrentAmount($piggyBank), $currency->decimal_places);
$currency = $piggyBank->transactionCurrency;
$currentAmount = app('steam')->bcround($this->piggyRepos->getCurrentAmount($piggyBank), $currency->decimal_places);
// Amounts, depending on 0.0 state of target amount
$percentage = null;
$targetAmount = $piggyBank->target_amount;
$leftToSave = null;
$savePerMonth = null;
$percentage = null;
$targetAmount = $piggyBank->target_amount;
$leftToSave = null;
$savePerMonth = null;
if (0 !== bccomp($targetAmount, '0')) { // target amount is not 0.00
$leftToSave = bcsub($piggyBank->target_amount, $currentAmount);
$percentage = (int) bcmul(bcdiv($currentAmount, $targetAmount), '100');
@ -92,16 +92,16 @@ class PiggyBankTransformer extends AbstractTransformer
$leftToSave = app('steam')->bcround($leftToSave, $currency->decimal_places);
$savePerMonth = app('steam')->bcround($this->piggyRepos->getSuggestedMonthlyAmount($piggyBank), $currency->decimal_places);
}
$startDate = $piggyBank->start_date?->format('Y-m-d');
$targetDate = $piggyBank->target_date?->format('Y-m-d');
$startDate = $piggyBank->start_date?->format('Y-m-d');
$targetDate = $piggyBank->target_date?->format('Y-m-d');
return [
'id' => (string) $piggyBank->id,
'created_at' => $piggyBank->created_at->toAtomString(),
'updated_at' => $piggyBank->updated_at->toAtomString(),
'accounts' => $this->renderAccounts($piggyBank),
//'account_id' => (string)$piggyBank->account_id,
//'account_name' => $piggyBank->account->name,
// 'account_id' => (string)$piggyBank->account_id,
// 'account_name' => $piggyBank->account->name,
'name' => $piggyBank->name,
'currency_id' => (string) $currency->id,
'currency_code' => $currency->code,
@ -123,7 +123,7 @@ class PiggyBankTransformer extends AbstractTransformer
'links' => [
[
'rel' => 'self',
'uri' => '/piggy_banks/' . $piggyBank->id,
'uri' => '/piggy_banks/'.$piggyBank->id,
],
],
];
@ -134,12 +134,13 @@ class PiggyBankTransformer extends AbstractTransformer
$return = [];
foreach ($piggyBank->accounts()->get() as $account) {
$return[] = [
'id' => $account->id,
'name' => $account->name,
'id' => $account->id,
'name' => $account->name,
'current_amount' => $account->pivot->current_amount,
// TODO add balance, add left to save.
];
}
return $return;
}
}

View File

@ -115,6 +115,7 @@ class PiggyBankTransformer extends AbstractTransformer
// grab repetitions (for current amount):
$repetitions = PiggyBankRepetition::whereIn('piggy_bank_id', $piggyBanks)->get();
throw new FireflyException('[d] Piggy bank repetitions are EOL.');
/** @var PiggyBankRepetition $repetition */

View File

@ -49,10 +49,8 @@ use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\UserGroup;
use FireflyIII\Models\UserRole;
use FireflyIII\Models\Webhook;
use FireflyIII\Notifications\Admin\UserInvitation;
use FireflyIII\Notifications\Admin\UserRegistration;
use FireflyIII\Notifications\Admin\VersionCheckResult;
use FireflyIII\Notifications\Test\TestNotificationDiscord;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
@ -260,38 +258,38 @@ class User extends Authenticatable
app('log')->debug(sprintf('in hasAnyRoleInGroup(%s)', implode(', ', $roles)));
/** @var Collection $dbRoles */
$dbRoles = UserRole::whereIn('title', $roles)->get();
$dbRoles = UserRole::whereIn('title', $roles)->get();
if (0 === $dbRoles->count()) {
app('log')->error(sprintf('Could not find role(s): %s. Probably migration mishap.', implode(', ', $roles)));
return false;
}
$dbRolesIds = $dbRoles->pluck('id')->toArray();
$dbRolesTitles = $dbRoles->pluck('title')->toArray();
$dbRolesIds = $dbRoles->pluck('id')->toArray();
$dbRolesTitles = $dbRoles->pluck('title')->toArray();
/** @var Collection $groupMemberships */
$groupMemberships = $this->groupMemberships()->whereIn('user_role_id', $dbRolesIds)->where('user_group_id', $userGroup->id)->get();
if (0 === $groupMemberships->count()) {
app('log')->error(sprintf(
'User #%d "%s" does not have roles %s in user group #%d "%s"',
$this->id,
$this->email,
implode(', ', $roles),
$userGroup->id,
$userGroup->title
));
'User #%d "%s" does not have roles %s in user group #%d "%s"',
$this->id,
$this->email,
implode(', ', $roles),
$userGroup->id,
$userGroup->title
));
return false;
}
foreach ($groupMemberships as $membership) {
app('log')->debug(sprintf(
'User #%d "%s" has role "%s" in user group #%d "%s"',
$this->id,
$this->email,
$membership->userRole->title,
$userGroup->id,
$userGroup->title
));
'User #%d "%s" has role "%s" in user group #%d "%s"',
$this->id,
$this->email,
$membership->userRole->title,
$userGroup->id,
$userGroup->title
));
if (in_array($membership->userRole->title, $dbRolesTitles, true)) {
app('log')->debug(sprintf('Return true, found role "%s"', $membership->userRole->title));
@ -299,13 +297,13 @@ class User extends Authenticatable
}
}
app('log')->error(sprintf(
'User #%d "%s" does not have roles %s in user group #%d "%s"',
$this->id,
$this->email,
implode(', ', $roles),
$userGroup->id,
$userGroup->title
));
'User #%d "%s" does not have roles %s in user group #%d "%s"',
$this->id,
$this->email,
implode(', ', $roles),
$userGroup->id,
$userGroup->title
));
return false;
}
@ -357,13 +355,13 @@ class User extends Authenticatable
*/
public function routeNotificationFor($driver, $notification = null)
{
$method = 'routeNotificationFor' . Str::studly($driver);
$method = 'routeNotificationFor'.Str::studly($driver);
if (method_exists($this, $method)) {
return $this->{$method}($notification); // @phpstan-ignore-line
}
$email = $this->email;
$email = $this->email;
// see if user has alternative email address:
$pref = app('preferences')->getForUser($this, 'remote_guard_alt_email');
$pref = app('preferences')->getForUser($this, 'remote_guard_alt_email');
if (null !== $pref) {
$email = $pref->data;
}
@ -400,36 +398,36 @@ class User extends Authenticatable
$appToken = (string) app('fireflyconfig')->getEncrypted('pushover_app_token', '')->data;
$userToken = (string) app('fireflyconfig')->getEncrypted('pushover_user_token', '')->data;
if (property_exists($notification, 'type') && $notification->type === 'owner') {
if (property_exists($notification, 'type') && 'owner' === $notification->type) {
return PushoverReceiver::withUserKey($userToken)
->withApplicationToken($appToken);
->withApplicationToken($appToken)
;
}
throw new FireflyException('No pushover token found.');
// return PushoverReceiver::withUserKey((string) config('services.pushover.user_token'))
// ->withApplicationToken((string) config('services.pushover.token'));
//return (string) config('services.pushover.token');
// return PushoverReceiver::withUserKey((string) config('services.pushover.user_token'))
// ->withApplicationToken((string) config('services.pushover.token'));
// return (string) config('services.pushover.token');
}
/**
* Route notifications for the Slack channel.
*/
public function routeNotificationForSlack(Notification $notification): ?string
{
// this check does not validate if the user is owner, Should be done by notification itself.
$res = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
$res = app('fireflyconfig')->getEncrypted('slack_webhook_url', '')->data;
if (is_array($res)) {
$res = '';
}
$res = (string) $res;
$res = (string) $res;
if (property_exists($notification, 'type') && $notification->type === 'owner') {
if (property_exists($notification, 'type') && 'owner' === $notification->type) {
return $res;
}
// not the best way to do this, but alas.
if ($notification instanceof UserRegistration) {
if ($notification instanceof UserRegistration) {
return $res;
}
if ($notification instanceof VersionCheckResult) {

View File

@ -813,14 +813,15 @@ class FireflyValidator extends Validator
public function validateUniquePiggyBankForUser($attribute, $value, $parameters): bool
{
$exclude = $parameters[0] ?? null;
$query = PiggyBank
::leftJoin('account_piggy_bank','account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
$query = PiggyBank::leftJoin('account_piggy_bank', 'account_piggy_bank.piggy_bank_id', '=', 'piggy_banks.id')
->leftJoin('accounts', 'accounts.id', '=', 'account_piggy_bank.account_id')
->where('accounts.user_id', auth()->user()->id);
->where('accounts.user_id', auth()->user()->id)
;
if (null !== $exclude) {
$query->where('piggy_banks.id', '!=', (int) $exclude);
}
$query->where('piggy_banks.name', $value);
return 0 === $query->get(['piggy_banks.*'])->count();
}

View File

@ -428,7 +428,7 @@ return [
'transfers' => 'fa-exchange',
],
'bindables' => [
'bindables' => [
// models
'account' => Account::class,
'attachment' => Attachment::class,
@ -486,7 +486,7 @@ return [
'userGroupBill' => UserGroupBill::class,
'userGroup' => UserGroup::class,
],
'rule-actions' => [
'rule-actions' => [
'set_category' => SetCategory::class,
'clear_category' => ClearCategory::class,
'set_budget' => SetBudget::class,
@ -520,7 +520,7 @@ return [
// 'set_foreign_amount' => SetForeignAmount::class,
// 'set_foreign_currency' => SetForeignCurrency::class,
],
'context-rule-actions' => [
'context-rule-actions' => [
'set_category',
'set_budget',
'add_tag',
@ -539,16 +539,16 @@ return [
'convert_transfer',
],
'test-triggers' => [
'test-triggers' => [
'limit' => 10,
'range' => 200,
],
// expected source types for each transaction type, in order of preference.
'expected_source_types' => [
'expected_source_types' => [
'source' => [
TransactionTypeEnum::WITHDRAWAL->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
TransactionTypeEnum::DEPOSIT->value => [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::CASH->value],
TransactionTypeEnum::DEPOSIT->value => [AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::REVENUE->value, AccountTypeEnum::CASH->value],
TransactionTypeEnum::TRANSFER->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
TransactionTypeEnum::OPENING_BALANCE->value => [
AccountTypeEnum::INITIAL_BALANCE->value,
@ -560,7 +560,7 @@ return [
TransactionTypeEnum::RECONCILIATION->value => [AccountTypeEnum::RECONCILIATION->value, AccountTypeEnum::ASSET->value],
TransactionTypeEnum::LIABILITY_CREDIT->value => [AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
// in case no transaction type is known yet, it could be anything.
'none' => [
'none' => [
AccountTypeEnum::ASSET->value,
AccountTypeEnum::EXPENSE->value,
AccountTypeEnum::REVENUE->value,
@ -577,7 +577,7 @@ return [
AccountTypeEnum::EXPENSE->value,
AccountTypeEnum::CASH->value,
],
TransactionTypeEnum::DEPOSIT->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
TransactionTypeEnum::DEPOSIT->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
TransactionTypeEnum::TRANSFER->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
TransactionTypeEnum::OPENING_BALANCE->value => [
AccountTypeEnum::INITIAL_BALANCE->value,
@ -590,7 +590,7 @@ return [
TransactionTypeEnum::LIABILITY_CREDIT->value => [AccountTypeEnum::LIABILITY_CREDIT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
],
],
'allowed_opposing_types' => [
'allowed_opposing_types' => [
'source' => [
AccountTypeEnum::ASSET->value => [
AccountTypeEnum::ASSET->value,
@ -680,7 +680,7 @@ return [
],
],
// depending on the account type, return the allowed transaction types:
'allowed_transaction_types' => [
'allowed_transaction_types' => [
'source' => [
AccountTypeEnum::ASSET->value => [
TransactionTypeEnum::WITHDRAWAL->value,
@ -749,7 +749,7 @@ return [
],
// having the source + dest will tell you the transaction type.
'account_to_transaction' => [
'account_to_transaction' => [
AccountTypeEnum::ASSET->value => [
AccountTypeEnum::ASSET->value => TransactionTypeEnum::TRANSFER->value,
AccountTypeEnum::CASH->value => TransactionTypeEnum::WITHDRAWAL->value,
@ -814,14 +814,14 @@ return [
],
// allowed source -> destination accounts.
'source_dests' => [
'source_dests' => [
TransactionTypeEnum::WITHDRAWAL->value => [
AccountTypeEnum::ASSET->value => [AccountTypeEnum::EXPENSE->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::CASH->value],
AccountTypeEnum::LOAN->value => [AccountTypeEnum::EXPENSE->value, AccountTypeEnum::CASH->value],
AccountTypeEnum::DEBT->value => [AccountTypeEnum::EXPENSE->value, AccountTypeEnum::CASH->value],
AccountTypeEnum::MORTGAGE->value => [AccountTypeEnum::EXPENSE->value, AccountTypeEnum::CASH->value],
],
TransactionTypeEnum::DEPOSIT->value => [
TransactionTypeEnum::DEPOSIT->value => [
AccountTypeEnum::REVENUE->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
AccountTypeEnum::CASH->value => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
AccountTypeEnum::LOAN->value => [AccountTypeEnum::ASSET->value],
@ -853,7 +853,7 @@ return [
],
],
// if you add fields to this array, don't forget to update the export routine (ExportDataGenerator).
'journal_meta_fields' => [
'journal_meta_fields' => [
// sepa
'sepa_cc',
'sepa_ct_op',
@ -887,31 +887,31 @@ return [
'recurrence_count',
'recurrence_date',
],
'webhooks' => [
'webhooks' => [
'max_attempts' => env('WEBHOOK_MAX_ATTEMPTS', 3),
],
'can_have_virtual_amounts' => [AccountTypeEnum::ASSET->value],
'can_have_opening_balance' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
'dynamic_creation_allowed' => [
'can_have_virtual_amounts' => [AccountTypeEnum::ASSET->value],
'can_have_opening_balance' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
'dynamic_creation_allowed' => [
AccountTypeEnum::EXPENSE->value,
AccountTypeEnum::REVENUE->value,
AccountTypeEnum::INITIAL_BALANCE->value,
AccountTypeEnum::RECONCILIATION->value,
AccountTypeEnum::LIABILITY_CREDIT->value,
],
'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'],
'valid_asset_fields' => ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_cc_fields' => ['account_role', 'cc_monthly_payment_date', 'cc_type', 'account_number', 'currency_id', 'BIC', 'include_net_worth'],
'valid_account_fields' => ['account_number', 'currency_id', 'BIC', 'interest', 'interest_period', 'include_net_worth', 'liability_direction'],
// dynamic date ranges are as follows:
'dynamic_date_ranges' => ['last7', 'last30', 'last90', 'last365', 'MTD', 'QTD', 'YTD'],
'dynamic_date_ranges' => ['last7', 'last30', 'last90', 'last365', 'MTD', 'QTD', 'YTD'],
// only used in v1
'allowed_sort_parameters' => ['order', 'name', 'iban'],
'allowed_sort_parameters' => ['order', 'name', 'iban'],
// preselected account lists possibilities:
'preselected_accounts' => ['all', 'assets', 'liabilities'],
'preselected_accounts' => ['all', 'assets', 'liabilities'],
// allowed to store a piggy bank in:
'piggy_bank_account_types' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
'piggy_bank_account_types' => [AccountTypeEnum::ASSET->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::MORTGAGE->value],
];

View File

@ -1,4 +1,5 @@
<?php
/*
* notifications.php
* Copyright (c) 2024 james@firefly-iii.org.
@ -20,14 +21,15 @@
*/
declare(strict_types=1);
return [
'channels' => [
'email' => ['enabled' => true, 'ui_configurable' => 0,],
'slack' => ['enabled' => true, 'ui_configurable' => 1,],
'ntfy' => ['enabled' => true, 'ui_configurable' => 1,],
'pushover' => ['enabled' => true, 'ui_configurable' => 1,],
'gotify' => ['enabled' => false, 'ui_configurable' => 0,],
'pushbullet' => ['enabled' => false, 'ui_configurable' => 0,],
'email' => ['enabled' => true, 'ui_configurable' => 0],
'slack' => ['enabled' => true, 'ui_configurable' => 1],
'ntfy' => ['enabled' => true, 'ui_configurable' => 1],
'pushover' => ['enabled' => true, 'ui_configurable' => 1],
'gotify' => ['enabled' => false, 'ui_configurable' => 0],
'pushbullet' => ['enabled' => false, 'ui_configurable' => 0],
],
'notifications' => [
'user' => [
@ -49,13 +51,13 @@ return [
'new_backup_codes' => ['enabled' => true, 'configurable' => false],
],
'owner' => [
//'invitation_created' => ['enabled' => true],
// 'invitation_created' => ['enabled' => true],
// 'some_notification' => ['enabled' => true],
'admin_new_reg' => ['enabled' => true],
'user_new_reg' => ['enabled' => true],
'new_version' => ['enabled' => true],
'invite_created' => ['enabled' => true],
'invite_redeemed' => ['enabled' => true],
'admin_new_reg' => ['enabled' => true],
'user_new_reg' => ['enabled' => true],
'new_version' => ['enabled' => true],
'invite_created' => ['enabled' => true],
'invite_redeemed' => ['enabled' => true],
'unknown_user_attempt' => ['enabled' => true],
],
],

View File

@ -1,12 +1,14 @@
<?php
declare(strict_types=1);
// config for Wijourdil/NtfyNotificationChannel
return [
'server' => 'https://ntfy.sh',
'topic' => '',
'server' => 'https://ntfy.sh',
'topic' => '',
'authentication' => [
'enabled' => false,
'enabled' => false,
'username' => '',
'password' => '',
],

View File

@ -61,8 +61,8 @@ return [
'mandrill' => [
'secret' => env('MANDRILL_SECRET'),
],
'pushover' => [
'token' => 'fake_token',
'pushover' => [
'token' => 'fake_token',
'user_token' => 'fake_token',
],
];

View File

@ -1,31 +1,32 @@
<?php
declare(strict_types=1);
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
return new class () extends Migration {
/**
* Run the migrations.
*/
public function up(): void
{
// make account_id nullable and the relation also nullable.
Schema::table('piggy_banks', static function (Blueprint $table) {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 1. drop index
$table->dropForeign('piggy_banks_account_id_foreign');
});
Schema::table('piggy_banks', static function (Blueprint $table) {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 2. make column nullable.
$table->unsignedInteger('account_id')->nullable()->change();
});
Schema::table('piggy_banks', static function (Blueprint $table) {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 3. add currency
$table->integer('transaction_currency_id', false, true)->after('account_id');
$table->foreign('transaction_currency_id','unique_currency')->references('id')->on('transaction_currencies')->onDelete('cascade');
$table->foreign('transaction_currency_id', 'unique_currency')->references('id')->on('transaction_currencies')->onDelete('cascade');
});
Schema::table('piggy_banks', static function (Blueprint $table) {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 4. rename columns
$table->renameColumn('targetamount', 'target_amount');
$table->renameColumn('startdate', 'start_date');
@ -33,13 +34,13 @@ return new class extends Migration
$table->renameColumn('startdate_tz', 'start_date_tz');
$table->renameColumn('targetdate_tz', 'target_date_tz');
});
Schema::table('piggy_banks', static function (Blueprint $table) {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 5. add new index
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('set null');
});
// rename some fields in piggy bank reps.
Schema::table('piggy_bank_repetitions', static function (Blueprint $table) {
Schema::table('piggy_bank_repetitions', static function (Blueprint $table): void {
// 6. rename columns
$table->renameColumn('currentamount', 'current_amount');
$table->renameColumn('startdate', 'start_date');
@ -49,14 +50,14 @@ return new class extends Migration
});
// create table account_piggy_bank
Schema::create('account_piggy_bank', static function (Blueprint $table) {
Schema::create('account_piggy_bank', static function (Blueprint $table): void {
$table->id();
$table->integer('account_id', false, true);
$table->integer('piggy_bank_id',false, true);
$table->integer('piggy_bank_id', false, true);
$table->decimal('current_amount', 32, 12)->default('0');
$table->foreign('account_id')->references('id')->on('accounts')->onDelete('cascade');
$table->foreign('piggy_bank_id')->references('id')->on('piggy_banks')->onDelete('cascade');
$table->unique(['account_id', 'piggy_bank_id'],'unique_piggy_save');
$table->unique(['account_id', 'piggy_bank_id'], 'unique_piggy_save');
});
}
@ -66,7 +67,7 @@ return new class extends Migration
*/
public function down(): void
{
Schema::table('piggy_banks', static function (Blueprint $table) {
Schema::table('piggy_banks', static function (Blueprint $table): void {
// 1. drop account index again.
$table->dropForeign('piggy_banks_account_id_foreign');
@ -89,7 +90,7 @@ return new class extends Migration
});
// rename some fields in piggy bank reps.
Schema::table('piggy_bank_repetitions', static function (Blueprint $table) {
Schema::table('piggy_bank_repetitions', static function (Blueprint $table): void {
// 6. rename columns
$table->renameColumn('current_amount', 'currentamount');
$table->renameColumn('start_date', 'startdate');

View File

@ -88,5 +88,5 @@ return [
'mfa_disableMFA' => 'Disable multi-factor authentication',
// notifications
'notification_index' => 'Owner notifications',
'notification_index' => 'Owner notifications',
];

View File

@ -26,160 +26,160 @@ declare(strict_types=1);
return [
// common items
'greeting' => 'Hi there,',
'closing' => 'Beep boop,',
'signature' => 'The Firefly III Mail Robot',
'footer_ps' => 'PS: This message was sent because a request from IP :ipAddress triggered it.',
'greeting' => 'Hi there,',
'closing' => 'Beep boop,',
'signature' => 'The Firefly III Mail Robot',
'footer_ps' => 'PS: This message was sent because a request from IP :ipAddress triggered it.',
// admin test
'admin_test_subject' => 'A test message from your Firefly III installation',
'admin_test_body' => 'This is a test message from your Firefly III instance. It was sent to :email.',
'admin_test_subject' => 'A test message from your Firefly III installation',
'admin_test_body' => 'This is a test message from your Firefly III instance. It was sent to :email.',
'admin_test_message' => 'This is a test message from your Firefly III instance over channel ":channel".',
// Ignore this comment
// invite
'invitation_created_subject' => 'An invitation has been created',
'invitation_created_body' => 'Admin user ":email" created a user invitation which can be used by whoever is behind email address ":invitee". The invite will be valid for 48hrs.',
'invite_user_subject' => 'You\'ve been invited to create a Firefly III account.',
'invitation_introduction' => 'You\'ve been invited to create a Firefly III account on **:host**. Firefly III is a personal, self-hosted, private personal finance manager. All the cool kids are using it.',
'invitation_invited_by' => 'You\'ve been invited by ":admin" and this invitation was sent to ":invitee". That\'s you, right?',
'invitation_url' => 'The invitation is valid for 48 hours and can be redeemed by surfing to [Firefly III](:url). Enjoy!',
'invitation_created_subject' => 'An invitation has been created',
'invitation_created_body' => 'Admin user ":email" created a user invitation which can be used by whoever is behind email address ":invitee". The invite will be valid for 48hrs.',
'invite_user_subject' => 'You\'ve been invited to create a Firefly III account.',
'invitation_introduction' => 'You\'ve been invited to create a Firefly III account on **:host**. Firefly III is a personal, self-hosted, private personal finance manager. All the cool kids are using it.',
'invitation_invited_by' => 'You\'ve been invited by ":admin" and this invitation was sent to ":invitee". That\'s you, right?',
'invitation_url' => 'The invitation is valid for 48 hours and can be redeemed by surfing to [Firefly III](:url). Enjoy!',
// new IP
'login_from_new_ip' => 'New login on Firefly III',
'slack_login_from_new_ip' => 'New Firefly III login from IP :ip (:host)',
'new_ip_body' => 'Firefly III detected a new login on your account from an unknown IP address. If you never logged in from the IP address below, or it has been more than six months ago, Firefly III will warn you.',
'new_ip_warning' => 'If you recognize this IP address or the login, you can ignore this message. If you didn\'t login, of if you have no idea what this is about, verify your password security, change it, and log out all other sessions. To do this, go to your profile page. Of course you have 2FA enabled already, right? Stay safe!',
'ip_address' => 'IP address',
'host_name' => 'Host',
'date_time' => 'Date + time',
'login_from_new_ip' => 'New login on Firefly III',
'slack_login_from_new_ip' => 'New Firefly III login from IP :ip (:host)',
'new_ip_body' => 'Firefly III detected a new login on your account from an unknown IP address. If you never logged in from the IP address below, or it has been more than six months ago, Firefly III will warn you.',
'new_ip_warning' => 'If you recognize this IP address or the login, you can ignore this message. If you didn\'t login, of if you have no idea what this is about, verify your password security, change it, and log out all other sessions. To do this, go to your profile page. Of course you have 2FA enabled already, right? Stay safe!',
'ip_address' => 'IP address',
'host_name' => 'Host',
'date_time' => 'Date + time',
// access token created
'access_token_created_subject' => 'A new access token was created',
'access_token_created_body' => 'Somebody (hopefully you) just created a new Firefly III API Access Token for your user account.',
'access_token_created_explanation' => 'With this token, they can access **all** of your financial records through the Firefly III API.',
'access_token_created_revoke' => 'If this wasn\'t you, please revoke this token as soon as possible at :url',
'access_token_created_subject' => 'A new access token was created',
'access_token_created_body' => 'Somebody (hopefully you) just created a new Firefly III API Access Token for your user account.',
'access_token_created_explanation' => 'With this token, they can access **all** of your financial records through the Firefly III API.',
'access_token_created_revoke' => 'If this wasn\'t you, please revoke this token as soon as possible at :url',
// unknown user login attempt
'unknown_user_subject' => 'An unknown user tried to log in',
'unknown_user_body' => 'An unknown user tried to log in to Firefly III. The email address they used was ":address".',
'unknown_user_message' => 'The email address they used was ":address".',
'unknown_user_subject' => 'An unknown user tried to log in',
'unknown_user_body' => 'An unknown user tried to log in to Firefly III. The email address they used was ":address".',
'unknown_user_message' => 'The email address they used was ":address".',
// registered
'registered_subject' => 'Welcome to Firefly III!',
'registered_subject_admin' => 'A new user has registered',
'admin_new_user_registered' => 'A new user has registered. User **:email** was given user ID #:id.',
'registered_welcome' => 'Welcome to [Firefly III](:address). Your registration has made it, and this email is here to confirm it. Yay!',
'registered_pw' => 'If you have forgotten your password already, please reset it using [the password reset tool](:address/password/reset).',
'registered_help' => 'There is a help-icon in the top right corner of each page. If you need help, click it!',
'registered_closing' => 'Enjoy!',
'registered_firefly_iii_link' => 'Firefly III:',
'registered_pw_reset_link' => 'Password reset:',
'registered_doc_link' => 'Documentation:',
'registered_subject' => 'Welcome to Firefly III!',
'registered_subject_admin' => 'A new user has registered',
'admin_new_user_registered' => 'A new user has registered. User **:email** was given user ID #:id.',
'registered_welcome' => 'Welcome to [Firefly III](:address). Your registration has made it, and this email is here to confirm it. Yay!',
'registered_pw' => 'If you have forgotten your password already, please reset it using [the password reset tool](:address/password/reset).',
'registered_help' => 'There is a help-icon in the top right corner of each page. If you need help, click it!',
'registered_closing' => 'Enjoy!',
'registered_firefly_iii_link' => 'Firefly III:',
'registered_pw_reset_link' => 'Password reset:',
'registered_doc_link' => 'Documentation:',
// Ignore this comment
// new version
'new_version_email_subject' => 'A new Firefly III version is available',
'new_version_email_subject' => 'A new Firefly III version is available',
// email change
'email_change_subject' => 'Your Firefly III email address has changed',
'email_change_body_to_new' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this message, please ignore and delete it.',
'email_change_body_to_old' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this to happen, you **must** follow the "undo"-link below to protect your account!',
'email_change_ignore' => 'If you initiated this change, you may safely ignore this message.',
'email_change_old' => 'The old email address was: :email',
'email_change_old_strong' => 'The old email address was: **:email**',
'email_change_new' => 'The new email address is: :email',
'email_change_new_strong' => 'The new email address is: **:email**',
'email_change_instructions' => 'You cannot use Firefly III until you confirm this change. Please follow the link below to do so.',
'email_change_undo_link' => 'To undo the change, follow this link:',
'email_change_subject' => 'Your Firefly III email address has changed',
'email_change_body_to_new' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this message, please ignore and delete it.',
'email_change_body_to_old' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this to happen, you **must** follow the "undo"-link below to protect your account!',
'email_change_ignore' => 'If you initiated this change, you may safely ignore this message.',
'email_change_old' => 'The old email address was: :email',
'email_change_old_strong' => 'The old email address was: **:email**',
'email_change_new' => 'The new email address is: :email',
'email_change_new_strong' => 'The new email address is: **:email**',
'email_change_instructions' => 'You cannot use Firefly III until you confirm this change. Please follow the link below to do so.',
'email_change_undo_link' => 'To undo the change, follow this link:',
// OAuth token created
'oauth_created_subject' => 'A new OAuth client has been created',
'oauth_created_body' => 'Somebody (hopefully you) just created a new Firefly III API OAuth Client for your user account. It\'s labeled ":name" and has callback URL `:url`.',
'oauth_created_explanation' => 'With this client, they can access **all** of your financial records through the Firefly III API.',
'oauth_created_undo' => 'If this wasn\'t you, please revoke this client as soon as possible at `:url`',
'oauth_created_subject' => 'A new OAuth client has been created',
'oauth_created_body' => 'Somebody (hopefully you) just created a new Firefly III API OAuth Client for your user account. It\'s labeled ":name" and has callback URL `:url`.',
'oauth_created_explanation' => 'With this client, they can access **all** of your financial records through the Firefly III API.',
'oauth_created_undo' => 'If this wasn\'t you, please revoke this client as soon as possible at `:url`',
// reset password
'reset_pw_subject' => 'Your password reset request',
'reset_pw_instructions' => 'Somebody tried to reset your password. If it was you, please follow the link below to do so.',
'reset_pw_warning' => '**PLEASE** verify that the link actually goes to the Firefly III you expect it to go!',
'reset_pw_subject' => 'Your password reset request',
'reset_pw_instructions' => 'Somebody tried to reset your password. If it was you, please follow the link below to do so.',
'reset_pw_warning' => '**PLEASE** verify that the link actually goes to the Firefly III you expect it to go!',
// error
'error_subject' => 'Caught an error in Firefly III',
'error_intro' => 'Firefly III v:version ran into an error: <span style="font-family: monospace;">:errorMessage</span>.',
'error_type' => 'The error was of type ":class".',
'error_timestamp' => 'The error occurred on/at: :time.',
'error_location' => 'This error occurred in file "<span style="font-family: monospace;">:file</span>" on line :line with code :code.',
'error_user' => 'The error was encountered by user #:id, <a href="mailto::email">:email</a>.',
'error_no_user' => 'There was no user logged in for this error or no user was detected.',
'error_ip' => 'The IP address related to this error is: :ip',
'error_url' => 'URL is: :url',
'error_user_agent' => 'User agent: :userAgent',
'error_stacktrace' => 'The full stacktrace is below. If you think this is a bug in Firefly III, you can forward this message to <a href="mailto:james@firefly-iii.org?subject=I%20found%20a%20bug!">james@firefly-iii.org</a>. This can help fix the bug you just encountered.',
'error_github_html' => 'If you prefer, you can also open a new issue on <a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a>.',
'error_github_text' => 'If you prefer, you can also open a new issue on https://github.com/firefly-iii/firefly-iii/issues.',
'error_stacktrace_below' => 'The full stacktrace is below:',
'error_headers' => 'The following headers may also be relevant:',
'error_post' => 'This was submitted by the user:',
'error_subject' => 'Caught an error in Firefly III',
'error_intro' => 'Firefly III v:version ran into an error: <span style="font-family: monospace;">:errorMessage</span>.',
'error_type' => 'The error was of type ":class".',
'error_timestamp' => 'The error occurred on/at: :time.',
'error_location' => 'This error occurred in file "<span style="font-family: monospace;">:file</span>" on line :line with code :code.',
'error_user' => 'The error was encountered by user #:id, <a href="mailto::email">:email</a>.',
'error_no_user' => 'There was no user logged in for this error or no user was detected.',
'error_ip' => 'The IP address related to this error is: :ip',
'error_url' => 'URL is: :url',
'error_user_agent' => 'User agent: :userAgent',
'error_stacktrace' => 'The full stacktrace is below. If you think this is a bug in Firefly III, you can forward this message to <a href="mailto:james@firefly-iii.org?subject=I%20found%20a%20bug!">james@firefly-iii.org</a>. This can help fix the bug you just encountered.',
'error_github_html' => 'If you prefer, you can also open a new issue on <a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a>.',
'error_github_text' => 'If you prefer, you can also open a new issue on https://github.com/firefly-iii/firefly-iii/issues.',
'error_stacktrace_below' => 'The full stacktrace is below:',
'error_headers' => 'The following headers may also be relevant:',
'error_post' => 'This was submitted by the user:',
// Ignore this comment
// report new journals
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
// bill warning
'bill_warning_subject_end_date' => 'Your bill ":name" is due to end in :diff days',
'bill_warning_subject_now_end_date' => 'Your bill ":name" is due to end TODAY',
'bill_warning_subject_extension_date' => 'Your bill ":name" is due to be extended or cancelled in :diff days',
'bill_warning_subject_now_extension_date' => 'Your bill ":name" is due to be extended or cancelled TODAY',
'bill_warning_end_date' => 'Your bill **":name"** is due to end on :date. This moment will pass in about **:diff days**.',
'bill_warning_extension_date' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass in about **:diff days**.',
'bill_warning_end_date_zero' => 'Your bill **":name"** is due to end on :date. This moment will pass **TODAY!**',
'bill_warning_extension_date_zero' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass **TODAY!**',
'bill_warning_please_action' => 'Please take the appropriate action.',
'bill_warning_subject_end_date' => 'Your bill ":name" is due to end in :diff days',
'bill_warning_subject_now_end_date' => 'Your bill ":name" is due to end TODAY',
'bill_warning_subject_extension_date' => 'Your bill ":name" is due to be extended or cancelled in :diff days',
'bill_warning_subject_now_extension_date' => 'Your bill ":name" is due to be extended or cancelled TODAY',
'bill_warning_end_date' => 'Your bill **":name"** is due to end on :date. This moment will pass in about **:diff days**.',
'bill_warning_extension_date' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass in about **:diff days**.',
'bill_warning_end_date_zero' => 'Your bill **":name"** is due to end on :date. This moment will pass **TODAY!**',
'bill_warning_extension_date_zero' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass **TODAY!**',
'bill_warning_please_action' => 'Please take the appropriate action.',
// user has enabled MFA
'enabled_mfa_subject' => 'You have enabled multi-factor authentication',
'enabled_mfa_slack' => 'You (:email) have enabled multi-factor authentication. Is this not correct? Check your settings!',
'have_enabled_mfa' => 'You have enabled multi-factor authentication on your Firefly III account ":email". This means that you will need to use an authenticator app to log in from now on.',
'enabled_mfa_warning' => 'If you did not enable this, please contact your administrator immediately or check out the Firefly III documentation.',
'enabled_mfa_subject' => 'You have enabled multi-factor authentication',
'enabled_mfa_slack' => 'You (:email) have enabled multi-factor authentication. Is this not correct? Check your settings!',
'have_enabled_mfa' => 'You have enabled multi-factor authentication on your Firefly III account ":email". This means that you will need to use an authenticator app to log in from now on.',
'enabled_mfa_warning' => 'If you did not enable this, please contact your administrator immediately or check out the Firefly III documentation.',
'disabled_mfa_subject' => 'You have disabled multi-factor authentication!',
'disabled_mfa_slack' => 'You (:email) have disabled multi-factor authentication. Is this not correct? Check your settings!',
'have_disabled_mfa' => 'You have disabled multi-factor authentication on your Firefly III account ":email".',
'disabled_mfa_warning' => 'If you did not disable this, please contact your administrator immediately or check out the Firefly III documentation.',
'disabled_mfa_subject' => 'You have disabled multi-factor authentication!',
'disabled_mfa_slack' => 'You (:email) have disabled multi-factor authentication. Is this not correct? Check your settings!',
'have_disabled_mfa' => 'You have disabled multi-factor authentication on your Firefly III account ":email".',
'disabled_mfa_warning' => 'If you did not disable this, please contact your administrator immediately or check out the Firefly III documentation.',
'new_backup_codes_subject' => 'You have generated new back-up codes',
'new_backup_codes_slack' => 'You (:email) have generated new back-up codes. These can be used to login to Firefly III. Is this not correct? Check your settings!',
'new_backup_codes_intro' => 'You (:email) have generated new back-up codes. These can be used to login to Firefly III if you lose access to your authenticator app.',
'new_backup_codes_warning' => 'Please store these codes in a safe place. If you lose them, you will not be able to log in to Firefly III. If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
'new_backup_codes_subject' => 'You have generated new back-up codes',
'new_backup_codes_slack' => 'You (:email) have generated new back-up codes. These can be used to login to Firefly III. Is this not correct? Check your settings!',
'new_backup_codes_intro' => 'You (:email) have generated new back-up codes. These can be used to login to Firefly III if you lose access to your authenticator app.',
'new_backup_codes_warning' => 'Please store these codes in a safe place. If you lose them, you will not be able to log in to Firefly III. If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
'used_backup_code_subject' => 'You have used a back-up code to login',
'used_backup_code_slack' => 'You (:email) have used a back-up code to login',
'used_backup_code_subject' => 'You have used a back-up code to login',
'used_backup_code_slack' => 'You (:email) have used a back-up code to login',
'used_backup_code_intro' => 'You (:email) have used a back-up code to login to Firefly III. You now have one less back-up code to login with. Please remove it from your list.',
'used_backup_code_warning' => 'If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
'used_backup_code_intro' => 'You (:email) have used a back-up code to login to Firefly III. You now have one less back-up code to login with. Please remove it from your list.',
'used_backup_code_warning' => 'If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
// few left:
'mfa_few_backups_left_subject' => 'You have only :count backup code(s) left!',
'mfa_few_backups_left_slack' => 'You (:email) have only :count backup code(s) left!',
'few_backup_codes_intro' => 'You (:email) have used most of your backup codes, and now have only :count left. Please generate new ones as soon as possible.',
'few_backup_codes_warning' => 'Without backup codes, you cannot recover your MFA login if you lose access to your code generator.',
'mfa_few_backups_left_subject' => 'You have only :count backup code(s) left!',
'mfa_few_backups_left_slack' => 'You (:email) have only :count backup code(s) left!',
'few_backup_codes_intro' => 'You (:email) have used most of your backup codes, and now have only :count left. Please generate new ones as soon as possible.',
'few_backup_codes_warning' => 'Without backup codes, you cannot recover your MFA login if you lose access to your code generator.',
// NO left:
'mfa_no_backups_left_subject' => 'You have NO backup codes left!',
'mfa_no_backups_left_slack' => 'You (:email) NO backup codes left!',
'no_backup_codes_intro' => 'You (:email) have used ALL of your backup codes. Please generate new ones as soon as possible.',
'no_backup_codes_warning' => 'Without backup codes, you cannot recover your MFA login if you lose access to your code generator.',
'mfa_no_backups_left_subject' => 'You have NO backup codes left!',
'mfa_no_backups_left_slack' => 'You (:email) NO backup codes left!',
'no_backup_codes_intro' => 'You (:email) have used ALL of your backup codes. Please generate new ones as soon as possible.',
'no_backup_codes_warning' => 'Without backup codes, you cannot recover your MFA login if you lose access to your code generator.',
// many failed MFA attempts
'mfa_many_failed_subject' => 'You have tried and failed to use multi-factor authentication :count times now!',
'mfa_many_failed_slack' => 'You (:email) have tried and failed to use multi-factor authentication :count times now. Is this not correct? Check your settings!',
'mfa_many_failed_attempts_intro' => 'You (:email) have tried :count times to use a multi-factor authentication code, but these login attempts have failed. Are you sure you are using the right MFA code? Are you sure the time on the server is correct?',
'mfa_many_failed_attempts_warning' => 'If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
'mfa_many_failed_subject' => 'You have tried and failed to use multi-factor authentication :count times now!',
'mfa_many_failed_slack' => 'You (:email) have tried and failed to use multi-factor authentication :count times now. Is this not correct? Check your settings!',
'mfa_many_failed_attempts_intro' => 'You (:email) have tried :count times to use a multi-factor authentication code, but these login attempts have failed. Are you sure you are using the right MFA code? Are you sure the time on the server is correct?',
'mfa_many_failed_attempts_warning' => 'If you did not do this, please contact your administrator immediately or check out the Firefly III documentation.',
];
// Ignore this comment

File diff suppressed because it is too large Load Diff

View File

@ -26,250 +26,250 @@ declare(strict_types=1);
return [
// new user:
'bank_name' => 'Bank name',
'bank_balance' => 'Balance',
'current_balance' => 'Current balance',
'savings_balance' => 'Savings balance',
'credit_card_limit' => 'Credit card limit',
'automatch' => 'Match automatically',
'skip' => 'Skip',
'enabled' => 'Enabled',
'name' => 'Name',
'active' => 'Active',
'amount_min' => 'Minimum amount',
'amount_max' => 'Maximum amount',
'match' => 'Matches on',
'strict' => 'Strict mode',
'repeat_freq' => 'Repeats',
'object_group' => 'Group',
'location' => 'Location',
'update_channel' => 'Update channel',
'currency_id' => 'Currency',
'transaction_currency_id' => 'Currency',
'auto_budget_currency_id' => 'Currency',
'external_ip' => 'Your server\'s external IP',
'attachments' => 'Attachments',
'BIC' => 'BIC',
'verify_password' => 'Verify password security',
'source_account' => 'Source account',
'destination_account' => 'Destination account',
'asset_destination_account' => 'Destination account',
'include_net_worth' => 'Include in net worth',
'asset_source_account' => 'Source account',
'journal_description' => 'Description',
'note' => 'Notes',
'currency' => 'Currency',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'bill_id' => 'Bill',
'opening_balance' => 'Opening balance',
'tagMode' => 'Tag mode',
'virtual_balance' => 'Virtual balance',
'bank_name' => 'Bank name',
'bank_balance' => 'Balance',
'current_balance' => 'Current balance',
'savings_balance' => 'Savings balance',
'credit_card_limit' => 'Credit card limit',
'automatch' => 'Match automatically',
'skip' => 'Skip',
'enabled' => 'Enabled',
'name' => 'Name',
'active' => 'Active',
'amount_min' => 'Minimum amount',
'amount_max' => 'Maximum amount',
'match' => 'Matches on',
'strict' => 'Strict mode',
'repeat_freq' => 'Repeats',
'object_group' => 'Group',
'location' => 'Location',
'update_channel' => 'Update channel',
'currency_id' => 'Currency',
'transaction_currency_id' => 'Currency',
'auto_budget_currency_id' => 'Currency',
'external_ip' => 'Your server\'s external IP',
'attachments' => 'Attachments',
'BIC' => 'BIC',
'verify_password' => 'Verify password security',
'source_account' => 'Source account',
'destination_account' => 'Destination account',
'asset_destination_account' => 'Destination account',
'include_net_worth' => 'Include in net worth',
'asset_source_account' => 'Source account',
'journal_description' => 'Description',
'note' => 'Notes',
'currency' => 'Currency',
'account_id' => 'Asset account',
'budget_id' => 'Budget',
'bill_id' => 'Bill',
'opening_balance' => 'Opening balance',
'tagMode' => 'Tag mode',
'virtual_balance' => 'Virtual balance',
// Ignore this comment
'targetamount' => 'Target amount',
'targetamount' => 'Target amount',
'target_amount' => 'Target amount',
'account_role' => 'Account role',
'opening_balance_date' => 'Opening balance date',
'cc_type' => 'Credit card payment plan',
'cc_monthly_payment_date' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank',
'returnHere' => 'Return here',
'returnHereExplanation' => 'After storing, return here to create another one.',
'returnHereUpdateExplanation' => 'After updating, return here.',
'description' => 'Description',
'expense_account' => 'Expense account',
'revenue_account' => 'Revenue account',
'decimal_places' => 'Decimal places',
'destination_amount' => 'Amount (destination)',
'new_email_address' => 'New email address',
'verification' => 'Verification',
'api_key' => 'API key',
'remember_me' => 'Remember me',
'liability_type_id' => 'Liability type',
'liability_type' => 'Liability type',
'interest' => 'Interest',
'interest_period' => 'Interest period',
'extension_date' => 'Extension date',
'type' => 'Type',
'convert_Withdrawal' => 'Convert withdrawal',
'convert_Deposit' => 'Convert deposit',
'convert_Transfer' => 'Convert transfer',
'amount' => 'Amount',
'foreign_amount' => 'Foreign amount',
'date' => 'Date',
'interest_date' => 'Interest date',
'book_date' => 'Book date',
'process_date' => 'Processing date',
'category' => 'Category',
'tags' => 'Tags',
'deletePermanently' => 'Delete permanently',
'cancel' => 'Cancel',
'targetdate' => 'Target date',
'account_role' => 'Account role',
'opening_balance_date' => 'Opening balance date',
'cc_type' => 'Credit card payment plan',
'cc_monthly_payment_date' => 'Credit card monthly payment date',
'piggy_bank_id' => 'Piggy bank',
'returnHere' => 'Return here',
'returnHereExplanation' => 'After storing, return here to create another one.',
'returnHereUpdateExplanation' => 'After updating, return here.',
'description' => 'Description',
'expense_account' => 'Expense account',
'revenue_account' => 'Revenue account',
'decimal_places' => 'Decimal places',
'destination_amount' => 'Amount (destination)',
'new_email_address' => 'New email address',
'verification' => 'Verification',
'api_key' => 'API key',
'remember_me' => 'Remember me',
'liability_type_id' => 'Liability type',
'liability_type' => 'Liability type',
'interest' => 'Interest',
'interest_period' => 'Interest period',
'extension_date' => 'Extension date',
'type' => 'Type',
'convert_Withdrawal' => 'Convert withdrawal',
'convert_Deposit' => 'Convert deposit',
'convert_Transfer' => 'Convert transfer',
'amount' => 'Amount',
'foreign_amount' => 'Foreign amount',
'date' => 'Date',
'interest_date' => 'Interest date',
'book_date' => 'Book date',
'process_date' => 'Processing date',
'category' => 'Category',
'tags' => 'Tags',
'deletePermanently' => 'Delete permanently',
'cancel' => 'Cancel',
'targetdate' => 'Target date',
'target_date' => 'Target date',
'startdate' => 'Start date',
'startdate' => 'Start date',
'start_date' => 'Start date',
'tag' => 'Tag',
'under' => 'Under',
'symbol' => 'Symbol',
'code' => 'Code',
'iban' => 'IBAN',
'account_number' => 'Account number',
'creditCardNumber' => 'Credit card number',
'has_headers' => 'Headers',
'date_format' => 'Date format',
'attachments[]' => 'Attachments',
'title' => 'Title',
'notes' => 'Notes',
'filename' => 'File name',
'mime' => 'Mime type',
'size' => 'Size',
'trigger' => 'Trigger',
'stop_processing' => 'Stop processing',
'end_date' => 'End date',
'enddate' => 'End date',
'move_rules_before_delete' => 'Rule group',
'start' => 'Start of range',
'end' => 'End of range',
'delete_account' => 'Delete account ":name"',
'delete_webhook' => 'Delete webhook ":title"',
'delete_bill' => 'Delete bill ":name"',
'delete_budget' => 'Delete budget ":name"',
'delete_category' => 'Delete category ":name"',
'delete_currency' => 'Delete currency ":name"',
'delete_journal' => 'Delete transaction with description ":description"',
'delete_attachment' => 'Delete attachment ":name"',
'delete_rule' => 'Delete rule ":title"',
'delete_rule_group' => 'Delete rule group ":title"',
'delete_link_type' => 'Delete link type ":name"',
'delete_user' => 'Delete user ":email"',
'delete_recurring' => 'Delete recurring transaction ":title"',
'user_areYouSure' => 'If you delete user ":email", everything will be gone. There is no undo, undelete or anything. If you delete yourself, you will lose access to this instance of Firefly III.',
'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
'account_areYouSure_js' => 'Are you sure you want to delete the account named "{name}"?',
'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?',
'object_group_areYouSure' => 'Are you sure you want to delete the group titled ":title"?',
'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?',
'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?',
'webhook_areYouSure' => 'Are you sure you want to delete the webhook named ":title"?',
'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?',
'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?',
'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?',
'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?',
'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?',
'tag' => 'Tag',
'under' => 'Under',
'symbol' => 'Symbol',
'code' => 'Code',
'iban' => 'IBAN',
'account_number' => 'Account number',
'creditCardNumber' => 'Credit card number',
'has_headers' => 'Headers',
'date_format' => 'Date format',
'attachments[]' => 'Attachments',
'title' => 'Title',
'notes' => 'Notes',
'filename' => 'File name',
'mime' => 'Mime type',
'size' => 'Size',
'trigger' => 'Trigger',
'stop_processing' => 'Stop processing',
'end_date' => 'End date',
'enddate' => 'End date',
'move_rules_before_delete' => 'Rule group',
'start' => 'Start of range',
'end' => 'End of range',
'delete_account' => 'Delete account ":name"',
'delete_webhook' => 'Delete webhook ":title"',
'delete_bill' => 'Delete bill ":name"',
'delete_budget' => 'Delete budget ":name"',
'delete_category' => 'Delete category ":name"',
'delete_currency' => 'Delete currency ":name"',
'delete_journal' => 'Delete transaction with description ":description"',
'delete_attachment' => 'Delete attachment ":name"',
'delete_rule' => 'Delete rule ":title"',
'delete_rule_group' => 'Delete rule group ":title"',
'delete_link_type' => 'Delete link type ":name"',
'delete_user' => 'Delete user ":email"',
'delete_recurring' => 'Delete recurring transaction ":title"',
'user_areYouSure' => 'If you delete user ":email", everything will be gone. There is no undo, undelete or anything. If you delete yourself, you will lose access to this instance of Firefly III.',
'attachment_areYouSure' => 'Are you sure you want to delete the attachment named ":name"?',
'account_areYouSure' => 'Are you sure you want to delete the account named ":name"?',
'account_areYouSure_js' => 'Are you sure you want to delete the account named "{name}"?',
'bill_areYouSure' => 'Are you sure you want to delete the bill named ":name"?',
'rule_areYouSure' => 'Are you sure you want to delete the rule titled ":title"?',
'object_group_areYouSure' => 'Are you sure you want to delete the group titled ":title"?',
'ruleGroup_areYouSure' => 'Are you sure you want to delete the rule group titled ":title"?',
'budget_areYouSure' => 'Are you sure you want to delete the budget named ":name"?',
'webhook_areYouSure' => 'Are you sure you want to delete the webhook named ":title"?',
'category_areYouSure' => 'Are you sure you want to delete the category named ":name"?',
'recurring_areYouSure' => 'Are you sure you want to delete the recurring transaction titled ":title"?',
'currency_areYouSure' => 'Are you sure you want to delete the currency named ":name"?',
'piggyBank_areYouSure' => 'Are you sure you want to delete the piggy bank named ":name"?',
'journal_areYouSure' => 'Are you sure you want to delete the transaction described ":description"?',
'mass_journal_are_you_sure' => 'Are you sure you want to delete these transactions?',
// Ignore this comment
'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
'journal_link_areYouSure' => 'Are you sure you want to delete the link between <a href=":source_link">:source</a> and <a href=":destination_link">:destination</a>?',
'linkType_areYouSure' => 'Are you sure you want to delete the link type ":name" (":inward" / ":outward")?',
'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.',
'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.',
'delete_all_permanently' => 'Delete selected permanently',
'update_all_journals' => 'Update these transactions',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
'also_delete_transactions_js' => 'No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.',
'also_delete_connections' => 'The only transaction linked with this link type will lose this connection.|All :count transactions linked with this link type will lose their connection.',
'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
'also_delete_piggyBanks_js' => 'No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.',
'not_delete_piggy_banks' => 'The piggy bank connected to this group will not be deleted.|The :count piggy banks connected to this group will not be deleted.',
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.',
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.',
'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.',
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.',
'check_for_updates' => 'Check for updates',
'liability_direction' => 'Liability in/out',
'delete_object_group' => 'Delete group ":title"',
'email' => 'Email address',
'password' => 'Password',
'password_confirmation' => 'Password (again)',
'blocked' => 'Is blocked?',
'blocked_code' => 'Reason for block',
'login_name' => 'Login',
'is_owner' => 'Is admin?',
'url' => 'URL',
'bill_end_date' => 'End date',
'tag_areYouSure' => 'Are you sure you want to delete the tag ":tag"?',
'journal_link_areYouSure' => 'Are you sure you want to delete the link between <a href=":source_link">:source</a> and <a href=":destination_link">:destination</a>?',
'linkType_areYouSure' => 'Are you sure you want to delete the link type ":name" (":inward" / ":outward")?',
'permDeleteWarning' => 'Deleting stuff from Firefly III is permanent and cannot be undone.',
'mass_make_selection' => 'You can still prevent items from being deleted by removing the checkbox.',
'delete_all_permanently' => 'Delete selected permanently',
'update_all_journals' => 'Update these transactions',
'also_delete_transactions' => 'The only transaction connected to this account will be deleted as well.|All :count transactions connected to this account will be deleted as well.',
'also_delete_transactions_js' => 'No transactions|The only transaction connected to this account will be deleted as well.|All {count} transactions connected to this account will be deleted as well.',
'also_delete_connections' => 'The only transaction linked with this link type will lose this connection.|All :count transactions linked with this link type will lose their connection.',
'also_delete_rules' => 'The only rule connected to this rule group will be deleted as well.|All :count rules connected to this rule group will be deleted as well.',
'also_delete_piggyBanks' => 'The only piggy bank connected to this account will be deleted as well.|All :count piggy bank connected to this account will be deleted as well.',
'also_delete_piggyBanks_js' => 'No piggy banks|The only piggy bank connected to this account will be deleted as well.|All {count} piggy banks connected to this account will be deleted as well.',
'not_delete_piggy_banks' => 'The piggy bank connected to this group will not be deleted.|The :count piggy banks connected to this group will not be deleted.',
'bill_keep_transactions' => 'The only transaction connected to this bill will not be deleted.|All :count transactions connected to this bill will be spared deletion.',
'budget_keep_transactions' => 'The only transaction connected to this budget will not be deleted.|All :count transactions connected to this budget will be spared deletion.',
'category_keep_transactions' => 'The only transaction connected to this category will not be deleted.|All :count transactions connected to this category will be spared deletion.',
'recurring_keep_transactions' => 'The only transaction created by this recurring transaction will not be deleted.|All :count transactions created by this recurring transaction will be spared deletion.',
'tag_keep_transactions' => 'The only transaction connected to this tag will not be deleted.|All :count transactions connected to this tag will be spared deletion.',
'check_for_updates' => 'Check for updates',
'liability_direction' => 'Liability in/out',
'delete_object_group' => 'Delete group ":title"',
'email' => 'Email address',
'password' => 'Password',
'password_confirmation' => 'Password (again)',
'blocked' => 'Is blocked?',
'blocked_code' => 'Reason for block',
'login_name' => 'Login',
'is_owner' => 'Is admin?',
'url' => 'URL',
'bill_end_date' => 'End date',
// import
'apply_rules' => 'Apply rules',
'artist' => 'Artist',
'album' => 'Album',
'song' => 'Song',
'apply_rules' => 'Apply rules',
'artist' => 'Artist',
'album' => 'Album',
'song' => 'Song',
// admin
'domain' => 'Domain',
'single_user_mode' => 'Disable user registration',
'is_demo_site' => 'Is demo site',
'domain' => 'Domain',
'single_user_mode' => 'Disable user registration',
'is_demo_site' => 'Is demo site',
// import
'configuration_file' => 'Configuration file',
'csv_comma' => 'A comma (,)',
'csv_semicolon' => 'A semicolon (;)',
'csv_tab' => 'A tab (invisible)',
'csv_delimiter' => 'CSV field delimiter',
'client_id' => 'Client ID',
'app_id' => 'App ID',
'secret' => 'Secret',
'public_key' => 'Public key',
'country_code' => 'Country code',
'provider_code' => 'Bank or data-provider',
'fints_url' => 'FinTS API URL',
'fints_port' => 'Port',
'fints_bank_code' => 'Bank code',
'fints_username' => 'Username',
'fints_password' => 'PIN / Password',
'fints_account' => 'FinTS account',
'local_account' => 'Firefly III account',
'configuration_file' => 'Configuration file',
'csv_comma' => 'A comma (,)',
'csv_semicolon' => 'A semicolon (;)',
'csv_tab' => 'A tab (invisible)',
'csv_delimiter' => 'CSV field delimiter',
'client_id' => 'Client ID',
'app_id' => 'App ID',
'secret' => 'Secret',
'public_key' => 'Public key',
'country_code' => 'Country code',
'provider_code' => 'Bank or data-provider',
'fints_url' => 'FinTS API URL',
'fints_port' => 'Port',
'fints_bank_code' => 'Bank code',
'fints_username' => 'Username',
'fints_password' => 'PIN / Password',
'fints_account' => 'FinTS account',
'local_account' => 'Firefly III account',
// Ignore this comment
'from_date' => 'Date from',
'to_date' => 'Date to',
'due_date' => 'Due date',
'payment_date' => 'Payment date',
'invoice_date' => 'Invoice date',
'internal_reference' => 'Internal reference',
'inward' => 'Inward description',
'outward' => 'Outward description',
'rule_group_id' => 'Rule group',
'transaction_description' => 'Transaction description',
'first_date' => 'First date',
'transaction_type' => 'Transaction type',
'repeat_until' => 'Repeat until',
'recurring_description' => 'Recurring transaction description',
'repetition_type' => 'Type of repetition',
'foreign_currency_id' => 'Foreign currency',
'repetition_end' => 'Repetition ends',
'repetitions' => 'Repetitions',
'calendar' => 'Calendar',
'weekend' => 'Weekend',
'client_secret' => 'Client secret',
'withdrawal_destination_id' => 'Destination account',
'deposit_source_id' => 'Source account',
'expected_on' => 'Expected on',
'paid' => 'Paid',
'auto_budget_type' => 'Auto-budget',
'auto_budget_amount' => 'Auto-budget amount',
'auto_budget_period' => 'Auto-budget period',
'collected' => 'Collected',
'submitted' => 'Submitted',
'key' => 'Key',
'value' => 'Content of record',
'webhook_delivery' => 'Delivery',
'webhook_response' => 'Response',
'webhook_trigger' => 'Trigger',
'pushover_app_token' => 'Pushover app token',
'pushover_user_token' => 'Pushover user token',
'ntfy_server' => 'Ntfy server',
'ntfy_topic' => 'Ntfy topic',
'ntfy_auth' => 'Ntfy authentication enabled',
'ntfy_user' => 'Ntfy username',
'ntfy_pass' => 'Ntfy password',
'from_date' => 'Date from',
'to_date' => 'Date to',
'due_date' => 'Due date',
'payment_date' => 'Payment date',
'invoice_date' => 'Invoice date',
'internal_reference' => 'Internal reference',
'inward' => 'Inward description',
'outward' => 'Outward description',
'rule_group_id' => 'Rule group',
'transaction_description' => 'Transaction description',
'first_date' => 'First date',
'transaction_type' => 'Transaction type',
'repeat_until' => 'Repeat until',
'recurring_description' => 'Recurring transaction description',
'repetition_type' => 'Type of repetition',
'foreign_currency_id' => 'Foreign currency',
'repetition_end' => 'Repetition ends',
'repetitions' => 'Repetitions',
'calendar' => 'Calendar',
'weekend' => 'Weekend',
'client_secret' => 'Client secret',
'withdrawal_destination_id' => 'Destination account',
'deposit_source_id' => 'Source account',
'expected_on' => 'Expected on',
'paid' => 'Paid',
'auto_budget_type' => 'Auto-budget',
'auto_budget_amount' => 'Auto-budget amount',
'auto_budget_period' => 'Auto-budget period',
'collected' => 'Collected',
'submitted' => 'Submitted',
'key' => 'Key',
'value' => 'Content of record',
'webhook_delivery' => 'Delivery',
'webhook_response' => 'Response',
'webhook_trigger' => 'Trigger',
'pushover_app_token' => 'Pushover app token',
'pushover_user_token' => 'Pushover user token',
'ntfy_server' => 'Ntfy server',
'ntfy_topic' => 'Ntfy topic',
'ntfy_auth' => 'Ntfy authentication enabled',
'ntfy_user' => 'Ntfy username',
'ntfy_pass' => 'Ntfy password',
];
// Ignore this comment

View File

@ -25,9 +25,9 @@
declare(strict_types=1);
return [
'invalid_account_type' => 'A piggy bank can only be linked to asset accounts and liabilities',
'invalid_account_currency' => 'This account does not use the currency you have selected',
'current_amount_too_much' => 'The combined amount in "current_amount" cannot exceed the "target_amount".',
'invalid_account_type' => 'A piggy bank can only be linked to asset accounts and liabilities',
'invalid_account_currency' => 'This account does not use the currency you have selected',
'current_amount_too_much' => 'The combined amount in "current_amount" cannot exceed the "target_amount".',
'filter_must_be_in' => 'Filter ":filter" must be one of: :values',
'filter_not_string' => 'Filter ":filter" is expected to be a string of text',
'bad_api_filter' => 'This API endpoint does not support ":filter" as a filter.',