mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-09-03 20:52:58 -05:00
🤖 Auto commit for release 'develop' on 2026-02-06
This commit is contained in:
@@ -123,16 +123,6 @@ class IndexController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function subtract(array $startBalances, array $endBalances): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($endBalances as $key => $value) {
|
||||
$result[$key] = bcsub((string) $value, $startBalances[$key] ?? '0');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show list of accounts.
|
||||
*
|
||||
@@ -214,4 +204,14 @@ class IndexController extends Controller
|
||||
'accounts' => $accounts,
|
||||
]);
|
||||
}
|
||||
|
||||
private function subtract(array $startBalances, array $endBalances): array
|
||||
{
|
||||
$result = [];
|
||||
foreach ($endBalances as $key => $value) {
|
||||
$result[$key] = bcsub((string) $value, $startBalances[$key] ?? '0');
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,29 +104,6 @@ class ForgotPasswordController extends Controller
|
||||
return back()->with('status', trans($response));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function validateHost(): void
|
||||
{
|
||||
try {
|
||||
$configuredHost = parse_url((string) config('app.url'), PHP_URL_HOST);
|
||||
} catch (UrlException $e) {
|
||||
throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.', 0, $e);
|
||||
}
|
||||
if (!is_string($configuredHost)) {
|
||||
throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.');
|
||||
}
|
||||
$host = request()->host();
|
||||
if ($configuredHost !== $host) {
|
||||
Log::error(sprintf('Host header is "%s", APP_URL is "%s".', $host, $configuredHost));
|
||||
|
||||
throw new FireflyException(
|
||||
'The Host-header does not match the host in the APP_URL environment variable. Please make sure these match. See also: https://bit.ly/FF3-host-header'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show form for email recovery.
|
||||
*
|
||||
@@ -155,4 +132,27 @@ class ForgotPasswordController extends Controller
|
||||
|
||||
return view('auth.passwords.email')->with(['allowRegistration' => $allowRegistration, 'pageTitle' => $pageTitle]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function validateHost(): void
|
||||
{
|
||||
try {
|
||||
$configuredHost = parse_url((string) config('app.url'), PHP_URL_HOST);
|
||||
} catch (UrlException $e) {
|
||||
throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.', 0, $e);
|
||||
}
|
||||
if (!is_string($configuredHost)) {
|
||||
throw new FireflyException('Please set a valid and correct Firefly III URL in the APP_URL environment variable.');
|
||||
}
|
||||
$host = request()->host();
|
||||
if ($configuredHost !== $host) {
|
||||
Log::error(sprintf('Host header is "%s", APP_URL is "%s".', $host, $configuredHost));
|
||||
|
||||
throw new FireflyException(
|
||||
'The Host-header does not match the host in the APP_URL environment variable. Please make sure these match. See also: https://bit.ly/FF3-host-header'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,29 +150,6 @@ class LoginController extends Controller
|
||||
return response()->json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the login username to be used by the controller.
|
||||
*/
|
||||
public function username(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request): void
|
||||
{
|
||||
$exception = ValidationException::withMessages([$this->username() => [trans('auth.failed')]]);
|
||||
$exception->redirectTo = route('login');
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log the user out of the application.
|
||||
*/
|
||||
@@ -257,6 +234,29 @@ class LoginController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the login username to be used by the controller.
|
||||
*/
|
||||
public function username(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the failed login response instance.
|
||||
*
|
||||
* @SuppressWarnings("PHPMD.UnusedFormalParameter")
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
protected function sendFailedLoginResponse(Request $request): void
|
||||
{
|
||||
$exception = ValidationException::withMessages([$this->username() => [trans('auth.failed')]]);
|
||||
$exception->redirectTo = route('login');
|
||||
|
||||
throw $exception;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the response after the user was authenticated.
|
||||
*
|
||||
|
||||
@@ -113,31 +113,6 @@ class RegisterController extends Controller
|
||||
return redirect($this->redirectPath());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function allowedToRegister(): bool
|
||||
{
|
||||
// is allowed to register?
|
||||
$allowRegistration = true;
|
||||
|
||||
try {
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
} catch (ContainerExceptionInterface|NotFoundExceptionInterface) {
|
||||
$singleUserMode = true;
|
||||
}
|
||||
$userCount = User::count();
|
||||
$guard = config('auth.defaults.guard');
|
||||
if (true === $singleUserMode && $userCount > 0 && 'web' === $guard) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
if ('web' !== $guard) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $allowRegistration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application registration form if the invitation code is valid.
|
||||
*
|
||||
@@ -197,4 +172,29 @@ class RegisterController extends Controller
|
||||
|
||||
return view('auth.register', ['isDemoSite' => $isDemoSite, 'email' => $email, 'pageTitle' => $pageTitle]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
protected function allowedToRegister(): bool
|
||||
{
|
||||
// is allowed to register?
|
||||
$allowRegistration = true;
|
||||
|
||||
try {
|
||||
$singleUserMode = FireflyConfig::get('single_user_mode', config('firefly.configuration.single_user_mode'))->data;
|
||||
} catch (ContainerExceptionInterface|NotFoundExceptionInterface) {
|
||||
$singleUserMode = true;
|
||||
}
|
||||
$userCount = User::count();
|
||||
$guard = config('auth.defaults.guard');
|
||||
if (true === $singleUserMode && $userCount > 0 && 'web' === $guard) {
|
||||
$allowRegistration = false;
|
||||
}
|
||||
if ('web' !== $guard) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $allowRegistration;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,22 +126,23 @@ class TwoFactorController extends Controller
|
||||
return redirect(route('home'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Each MFA history has a timestamp and a code, saving the MFA entries for 5 minutes. So if the
|
||||
* submitted MFA code has been submitted in the last 5 minutes, it won't work despite being valid.
|
||||
*/
|
||||
private function inMFAHistory(string $mfaCode, array $mfaHistory): bool
|
||||
private function addToMFAFailureCounter(): void
|
||||
{
|
||||
$now = Carbon::now()->getTimestamp();
|
||||
foreach ($mfaHistory as $entry) {
|
||||
$time = $entry['time'];
|
||||
$code = $entry['code'];
|
||||
if ($code === $mfaCode && ($now - $time) <= 300) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
$preference = (int) Preferences::get('mfa_failure_count', 0)->data;
|
||||
++$preference;
|
||||
Log::channel('audit')->info(sprintf('MFA failure count is set to %d.', $preference));
|
||||
Preferences::set('mfa_failure_count', $preference);
|
||||
}
|
||||
|
||||
return false;
|
||||
private function addToMFAHistory(string $mfaCode): void
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = Preferences::get('mfa_history', [])->data;
|
||||
$entry = ['time' => Carbon::now()->getTimestamp(), 'code' => $mfaCode];
|
||||
$mfaHistory[] = $entry;
|
||||
|
||||
Preferences::set('mfa_history', $mfaHistory);
|
||||
$this->filterMFAHistory();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,14 +164,6 @@ class TwoFactorController extends Controller
|
||||
Preferences::set('mfa_history', $newHistory);
|
||||
}
|
||||
|
||||
private function addToMFAFailureCounter(): void
|
||||
{
|
||||
$preference = (int) Preferences::get('mfa_failure_count', 0)->data;
|
||||
++$preference;
|
||||
Log::channel('audit')->info(sprintf('MFA failure count is set to %d.', $preference));
|
||||
Preferences::set('mfa_failure_count', $preference);
|
||||
}
|
||||
|
||||
private function getMFAFailureCounter(): int
|
||||
{
|
||||
$value = (int) Preferences::get('mfa_failure_count', 0)->data;
|
||||
@@ -179,21 +172,22 @@ class TwoFactorController extends Controller
|
||||
return $value;
|
||||
}
|
||||
|
||||
private function addToMFAHistory(string $mfaCode): void
|
||||
/**
|
||||
* Each MFA history has a timestamp and a code, saving the MFA entries for 5 minutes. So if the
|
||||
* submitted MFA code has been submitted in the last 5 minutes, it won't work despite being valid.
|
||||
*/
|
||||
private function inMFAHistory(string $mfaCode, array $mfaHistory): bool
|
||||
{
|
||||
/** @var array $mfaHistory */
|
||||
$mfaHistory = Preferences::get('mfa_history', [])->data;
|
||||
$entry = ['time' => Carbon::now()->getTimestamp(), 'code' => $mfaCode];
|
||||
$mfaHistory[] = $entry;
|
||||
$now = Carbon::now()->getTimestamp();
|
||||
foreach ($mfaHistory as $entry) {
|
||||
$time = $entry['time'];
|
||||
$code = $entry['code'];
|
||||
if ($code === $mfaCode && ($now - $time) <= 300) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Preferences::set('mfa_history', $mfaHistory);
|
||||
$this->filterMFAHistory();
|
||||
}
|
||||
|
||||
private function resetMFAFailureCounter(): void
|
||||
{
|
||||
Preferences::set('mfa_failure_count', 0);
|
||||
Log::channel('audit')->info('MFA failure count is set to zero.');
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -235,4 +229,10 @@ class TwoFactorController extends Controller
|
||||
|
||||
Preferences::set('mfa_recovery', $newList);
|
||||
}
|
||||
|
||||
private function resetMFAFailureCounter(): void
|
||||
{
|
||||
Preferences::set('mfa_failure_count', 0);
|
||||
Log::channel('audit')->info('MFA failure count is set to zero.');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,58 @@ class IndexController extends Controller
|
||||
return view('bills.index', ['bills' => $bills, 'sums' => $sums, 'total' => $total, 'totals' => $totals, 'today' => $today]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a bill.
|
||||
*/
|
||||
public function setOrder(Request $request, Bill $bill): JsonResponse
|
||||
{
|
||||
$objectGroupTitle = (string) $request->get('objectGroupTitle');
|
||||
$newOrder = (int) $request->get('order');
|
||||
$this->repository->setOrder($bill, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->repository->setObjectGroup($bill, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->repository->removeObjectGroup($bill);
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
|
||||
private function amountPerPeriod(array $bill, string $range): string
|
||||
{
|
||||
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
|
||||
|
||||
Log::debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name']));
|
||||
Log::debug(sprintf('Average is %s', $avg));
|
||||
// calculate amount per year:
|
||||
$multiplies = ['yearly' => '1', 'half-year' => '2', 'quarterly' => '4', 'monthly' => '12', 'weekly' => '52.17', 'daily' => '365.24'];
|
||||
$yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
|
||||
Log::debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
|
||||
|
||||
// per period:
|
||||
$division = [
|
||||
'1Y' => '1',
|
||||
'6M' => '2',
|
||||
'3M' => '4',
|
||||
'1M' => '12',
|
||||
'1W' => '52.16',
|
||||
'1D' => '365.24',
|
||||
'YTD' => '1',
|
||||
'QTD' => '4',
|
||||
'MTD' => '12',
|
||||
'last7' => '52.16',
|
||||
'last30' => '12',
|
||||
'last90' => '4',
|
||||
'last365' => '1',
|
||||
];
|
||||
$perPeriod = bcdiv($yearAmount, $division[$range]);
|
||||
|
||||
Log::debug(sprintf('Amount per %s is %s (%s / %s)', $range, $perPeriod, $yearAmount, $division[$range]));
|
||||
|
||||
return $perPeriod;
|
||||
}
|
||||
|
||||
private function getSums(array $bills): array
|
||||
{
|
||||
Log::debug(sprintf('now in getSums(count:%d)', count($bills)));
|
||||
@@ -220,40 +272,6 @@ class IndexController extends Controller
|
||||
return $sums;
|
||||
}
|
||||
|
||||
private function amountPerPeriod(array $bill, string $range): string
|
||||
{
|
||||
$avg = bcdiv(bcadd((string) $bill['amount_min'], (string) $bill['amount_max']), '2');
|
||||
|
||||
Log::debug(sprintf('Amount per period for bill #%d "%s"', $bill['id'], $bill['name']));
|
||||
Log::debug(sprintf('Average is %s', $avg));
|
||||
// calculate amount per year:
|
||||
$multiplies = ['yearly' => '1', 'half-year' => '2', 'quarterly' => '4', 'monthly' => '12', 'weekly' => '52.17', 'daily' => '365.24'];
|
||||
$yearAmount = bcmul($avg, bcdiv($multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
|
||||
Log::debug(sprintf('Amount per year is %s (%s * %s / %s)', $yearAmount, $avg, $multiplies[$bill['repeat_freq']], (string) ($bill['skip'] + 1)));
|
||||
|
||||
// per period:
|
||||
$division = [
|
||||
'1Y' => '1',
|
||||
'6M' => '2',
|
||||
'3M' => '4',
|
||||
'1M' => '12',
|
||||
'1W' => '52.16',
|
||||
'1D' => '365.24',
|
||||
'YTD' => '1',
|
||||
'QTD' => '4',
|
||||
'MTD' => '12',
|
||||
'last7' => '52.16',
|
||||
'last30' => '12',
|
||||
'last90' => '4',
|
||||
'last365' => '1',
|
||||
];
|
||||
$perPeriod = bcdiv($yearAmount, $division[$range]);
|
||||
|
||||
Log::debug(sprintf('Amount per %s is %s (%s / %s)', $range, $perPeriod, $yearAmount, $division[$range]));
|
||||
|
||||
return $perPeriod;
|
||||
}
|
||||
|
||||
private function getTotals(array $sums): array
|
||||
{
|
||||
$totals = [];
|
||||
@@ -287,22 +305,4 @@ class IndexController extends Controller
|
||||
|
||||
return $totals;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a bill.
|
||||
*/
|
||||
public function setOrder(Request $request, Bill $bill): JsonResponse
|
||||
{
|
||||
$objectGroupTitle = (string) $request->get('objectGroupTitle');
|
||||
$newOrder = (int) $request->get('order');
|
||||
$this->repository->setOrder($bill, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->repository->setObjectGroup($bill, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->repository->removeObjectGroup($bill);
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +97,7 @@ class BudgetLimitController extends Controller
|
||||
return true;
|
||||
});
|
||||
|
||||
return view('budgets.budget-limits.create', ['start' => $start, 'end' => $end, 'currencies' => $currencies, 'budget' => $budget]);
|
||||
return view('budgets.budget-limits.create', ['start' => $start, 'end' => $end, 'currencies' => $currencies, 'budget' => $budget]);
|
||||
}
|
||||
|
||||
public function delete(BudgetLimit $budgetLimit): Redirector|RedirectResponse
|
||||
|
||||
@@ -158,6 +158,24 @@ final class IndexController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse
|
||||
{
|
||||
$this->abRepository->cleanup();
|
||||
$budgetIds = $request->get('budgetIds');
|
||||
|
||||
foreach ($budgetIds as $index => $budgetId) {
|
||||
$budgetId = (int) $budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if ($budget instanceof Budget) {
|
||||
Log::debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1));
|
||||
$repository->setBudgetOrder($budget, $index + 1);
|
||||
}
|
||||
}
|
||||
Preferences::mark();
|
||||
|
||||
return response()->json(['OK']);
|
||||
}
|
||||
|
||||
private function getAllAvailableBudgets(Carbon $start, Carbon $end): array
|
||||
{
|
||||
Log::debug(sprintf('Start of getAllAvailableBudgets("%s", "%s")', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
|
||||
@@ -305,22 +323,4 @@ final class IndexController extends Controller
|
||||
|
||||
return $sums;
|
||||
}
|
||||
|
||||
public function reorder(Request $request, BudgetRepositoryInterface $repository): JsonResponse
|
||||
{
|
||||
$this->abRepository->cleanup();
|
||||
$budgetIds = $request->get('budgetIds');
|
||||
|
||||
foreach ($budgetIds as $index => $budgetId) {
|
||||
$budgetId = (int) $budgetId;
|
||||
$budget = $repository->find($budgetId);
|
||||
if ($budget instanceof Budget) {
|
||||
Log::debug(sprintf('Set budget #%d ("%s") to position %d', $budget->id, $budget->name, $index + 1));
|
||||
$repository->setBudgetOrder($budget, $index + 1);
|
||||
}
|
||||
}
|
||||
Preferences::mark();
|
||||
|
||||
return response()->json(['OK']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,17 +204,6 @@ class AccountController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expenses per budget for all time, as shown on account overview.
|
||||
*/
|
||||
public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
|
||||
{
|
||||
$start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
|
||||
$end = today(config('app.timezone'));
|
||||
|
||||
return $this->expenseBudget($account, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expenses per budget, as shown on account overview.
|
||||
*/
|
||||
@@ -293,14 +282,14 @@ class AccountController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Expenses grouped by category for account.
|
||||
* Expenses per budget for all time, as shown on account overview.
|
||||
*/
|
||||
public function expenseCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
|
||||
public function expenseBudgetAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
|
||||
{
|
||||
$start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
|
||||
$end = today(config('app.timezone'));
|
||||
|
||||
return $this->expenseCategory($account, $start, $end);
|
||||
return $this->expenseBudget($account, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -375,6 +364,17 @@ class AccountController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Expenses grouped by category for account.
|
||||
*/
|
||||
public function expenseCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
|
||||
{
|
||||
$start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
|
||||
$end = today(config('app.timezone'));
|
||||
|
||||
return $this->expenseCategory($account, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for all the user's frontpage accounts.
|
||||
*
|
||||
@@ -401,17 +401,6 @@ class AccountController extends Controller
|
||||
return response()->json($this->accountBalanceChart($accounts, $start, $end));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the income grouped by category for an account, in all time.
|
||||
*/
|
||||
public function incomeCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
|
||||
{
|
||||
$start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
|
||||
$end = today(config('app.timezone'));
|
||||
|
||||
return $this->incomeCategory($account, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows all income per account for each category.
|
||||
*/
|
||||
@@ -485,6 +474,17 @@ class AccountController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the income grouped by category for an account, in all time.
|
||||
*/
|
||||
public function incomeCategoryAll(AccountRepositoryInterface $repository, Account $account): JsonResponse
|
||||
{
|
||||
$start = $repository->oldestJournalDate($account) ?? today(config('app.timezone'))->startOfMonth();
|
||||
$end = today(config('app.timezone'));
|
||||
|
||||
return $this->incomeCategory($account, $start, $end);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows overview of account during a single period.
|
||||
*
|
||||
@@ -617,15 +617,6 @@ class AccountController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function updateChartKeys(array $array, array $balances): array
|
||||
{
|
||||
foreach (array_keys($balances) as $key) {
|
||||
$array[$key] ??= ['key' => $key];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the balances for a given set of dates and accounts.
|
||||
*
|
||||
@@ -762,4 +753,13 @@ class AccountController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function updateChartKeys(array $array, array $balances): array
|
||||
{
|
||||
foreach (array_keys($balances) as $key) {
|
||||
$array[$key] ??= ['key' => $key];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,23 +196,6 @@ class BudgetReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart that groups expenses by the account.
|
||||
*/
|
||||
@@ -242,4 +225,21 @@ class BudgetReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,11 +97,6 @@ class CategoryController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function getDate(): Carbon
|
||||
{
|
||||
return today(config('app.timezone'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the category chart on the front page.
|
||||
* TODO test method for category refactor.
|
||||
@@ -151,6 +146,67 @@ class CategoryController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart for period for transactions without a category.
|
||||
* TODO test me.
|
||||
*/
|
||||
public function reportPeriodNoCategory(Collection $accounts, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.category.period.no-cat');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
}
|
||||
$data = $this->reportPeriodChart($accounts, $start, $end, null);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart for a specific period.
|
||||
* TODO test me, for category refactor.
|
||||
*
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function specificPeriod(Category $category, Carbon $date): JsonResponse
|
||||
{
|
||||
$range = Navigation::getViewRange(false);
|
||||
$start = Navigation::startOfPeriod($date, $range);
|
||||
$end = session()->get('end');
|
||||
if ($end < $start) {
|
||||
[$end, $start] = [$start, $end];
|
||||
}
|
||||
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('chart.category.period-chart');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
}
|
||||
|
||||
/** @var WholePeriodChartGenerator $chartGenerator */
|
||||
$chartGenerator = app(WholePeriodChartGenerator::class);
|
||||
$chartData = $chartGenerator->generate($category, $start, $end);
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
private function getDate(): Carbon
|
||||
{
|
||||
return today(config('app.timezone'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate report chart for either with or without category.
|
||||
*/
|
||||
@@ -227,60 +283,4 @@ class CategoryController extends Controller
|
||||
|
||||
return $this->generator->multiSet($chartData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart for period for transactions without a category.
|
||||
* TODO test me.
|
||||
*/
|
||||
public function reportPeriodNoCategory(Collection $accounts, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.category.period.no-cat');
|
||||
$cache->addProperty($accounts->pluck('id')->toArray());
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
}
|
||||
$data = $this->reportPeriodChart($accounts, $start, $end, null);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chart for a specific period.
|
||||
* TODO test me, for category refactor.
|
||||
*
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function specificPeriod(Category $category, Carbon $date): JsonResponse
|
||||
{
|
||||
$range = Navigation::getViewRange(false);
|
||||
$start = Navigation::startOfPeriod($date, $range);
|
||||
$end = session()->get('end');
|
||||
if ($end < $start) {
|
||||
[$end, $start] = [$start, $end];
|
||||
}
|
||||
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty($category->id);
|
||||
$cache->addProperty('chart.category.period-chart');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get());
|
||||
}
|
||||
|
||||
/** @var WholePeriodChartGenerator $chartGenerator */
|
||||
$chartGenerator = app(WholePeriodChartGenerator::class);
|
||||
$chartData = $chartGenerator->generate($category, $start, $end);
|
||||
$data = $this->generator->multiSet($chartData);
|
||||
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -268,26 +268,6 @@ class CategoryReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function sourceExpense(Collection $accounts, Collection $categories, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$result = [];
|
||||
@@ -343,4 +323,24 @@ class CategoryReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,44 +213,6 @@ class DoubleReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function tagExpense(Collection $accounts, Collection $others, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$result = [];
|
||||
@@ -356,4 +318,42 @@ class DoubleReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,26 +274,6 @@ class TagReportController extends Controller
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public function sourceExpense(Collection $accounts, Collection $tags, Carbon $start, Carbon $end): JsonResponse
|
||||
{
|
||||
$result = [];
|
||||
@@ -401,4 +381,24 @@ class TagReportController extends Controller
|
||||
|
||||
return response()->json($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate function
|
||||
*/
|
||||
private function makeEntries(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$format = Navigation::preferredCarbonLocalizedFormat($start, $end);
|
||||
$preferredRange = Navigation::preferredRangeFormat($start, $end);
|
||||
$currentStart = clone $start;
|
||||
while ($currentStart <= $end) {
|
||||
$currentEnd = Navigation::endOfPeriod($currentStart, $preferredRange);
|
||||
$key = $currentStart->isoFormat($format);
|
||||
$return[$key] = '0';
|
||||
$currentStart = clone $currentEnd;
|
||||
$currentStart->addDay()->startOfDay();
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@ class DebugController extends Controller
|
||||
$this->middleware(IsDemoUser::class)->except(['displayError']);
|
||||
}
|
||||
|
||||
public function apiTest(): View
|
||||
{
|
||||
return view('test.api-test');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all possible errors.
|
||||
*
|
||||
@@ -162,200 +167,6 @@ class DebugController extends Controller
|
||||
return view('debug', ['table' => $table, 'now' => $now, 'logContent' => $logContent]);
|
||||
}
|
||||
|
||||
public function apiTest(): View
|
||||
{
|
||||
return view('test.api-test');
|
||||
}
|
||||
|
||||
private function generateTable(): string
|
||||
{
|
||||
// system information:
|
||||
$system = $this->getSystemInformation();
|
||||
$docker = $this->getBuildInfo();
|
||||
$app = $this->getAppInfo();
|
||||
$user = $this->getUserInfo();
|
||||
|
||||
return (string) view('partials.debug-table', ['system' => $system, 'docker' => $docker, 'app' => $app, 'user' => $user]);
|
||||
}
|
||||
|
||||
private function getSystemInformation(): array
|
||||
{
|
||||
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||
$drivers = DB::availableDrivers();
|
||||
$currentDriver = DB::getDriverName();
|
||||
|
||||
return [
|
||||
'php_version' => PHP_VERSION,
|
||||
'php_os' => PHP_OS,
|
||||
'build_time' => config('firefly.build_time'),
|
||||
'build_time_nice' => Carbon::parse(config('firefly.build_time'), 'Europe/Amsterdam')->setTimezone('Europe/Amsterdam')->format('Y-m-d H:i:s e'),
|
||||
'uname' => php_uname('m'),
|
||||
'interface' => PHP_SAPI,
|
||||
'bits' => PHP_INT_SIZE * 8,
|
||||
'bcscale' => bcscale(),
|
||||
'display_errors' => ini_get('display_errors'),
|
||||
'error_reporting' => $this->errorReporting((int) ini_get('error_reporting')),
|
||||
'upload_size' => min($maxFileSize, $maxPostSize),
|
||||
'all_drivers' => $drivers,
|
||||
'current_driver' => $currentDriver,
|
||||
];
|
||||
}
|
||||
|
||||
private function getBuildInfo(): array
|
||||
{
|
||||
$return = [
|
||||
'is_docker' => env('IS_DOCKER', false), // @phpstan-ignore-line
|
||||
'build' => '(unknown)',
|
||||
'build_date' => '(unknown)',
|
||||
'base_build' => '(unknown)',
|
||||
'base_build_date' => '(unknown)',
|
||||
];
|
||||
|
||||
try {
|
||||
if (file_exists('/var/www/counter-main.txt')) {
|
||||
$return['build'] = trim(file_get_contents('/var/www/counter-main.txt'));
|
||||
Log::debug(sprintf('build is now "%s"', $return['build']));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::debug('Could not check build counter, but thats ok.');
|
||||
Log::warning($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (file_exists('/var/www/build-date-main.txt')) {
|
||||
$return['build_date'] = trim(file_get_contents('/var/www/build-date-main.txt'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::debug('Could not check build date, but thats ok.');
|
||||
Log::warning($e->getMessage());
|
||||
}
|
||||
if ('' !== (string) env('BASE_IMAGE_BUILD')) { // @phpstan-ignore-line
|
||||
$return['base_build'] = env('BASE_IMAGE_BUILD'); // @phpstan-ignore-line
|
||||
}
|
||||
if ('' !== (string) env('BASE_IMAGE_DATE')) { // @phpstan-ignore-line
|
||||
$return['base_build_date'] = env('BASE_IMAGE_DATE'); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getAppInfo(): array
|
||||
{
|
||||
$userGuard = config('auth.defaults.guard');
|
||||
|
||||
$config = FireflyConfig::get('last_rt_job', 0);
|
||||
$lastTime = (int) $config->data;
|
||||
$lastCronjob = 'never';
|
||||
$lastCronjobAgo = 'never';
|
||||
if ($lastTime > 0) {
|
||||
$carbon = Carbon::createFromTimestamp($lastTime);
|
||||
$lastCronjob = $carbon->format('Y-m-d H:i:s');
|
||||
$lastCronjobAgo = $carbon->locale('en')->diffForHumans(); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
return [
|
||||
'debug' => var_export(config('app.debug'), true),
|
||||
'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
|
||||
'default_language' => (string) config('firefly.default_language'),
|
||||
'default_locale' => (string) config('firefly.default_locale'),
|
||||
'remote_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_header') : 'N/A',
|
||||
'remote_mail_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_email') : 'N/A',
|
||||
'stateful_domains' => implode(', ', config('sanctum.stateful')),
|
||||
|
||||
// the dates for the cron job are based on the recurring cron job's times.
|
||||
// any of the cron jobs will do, they always run at the same time.
|
||||
// but this job is the oldest, so the biggest chance it ran once
|
||||
|
||||
'last_cronjob' => $lastCronjob,
|
||||
'last_cronjob_ago' => $lastCronjobAgo,
|
||||
];
|
||||
}
|
||||
|
||||
private function getUserInfo(): array
|
||||
{
|
||||
$userFlags = $this->getUserFlags();
|
||||
|
||||
// user info
|
||||
$userAgent = request()->header('user-agent');
|
||||
|
||||
// set languages, see what happens:
|
||||
$original = setlocale(LC_ALL, '0');
|
||||
$localeAttempts = [];
|
||||
$parts = Steam::getLocaleArray(Steam::getLocale());
|
||||
foreach ($parts as $code) {
|
||||
$code = trim($code);
|
||||
Log::debug(sprintf('Trying to set %s', $code));
|
||||
$result = setlocale(LC_ALL, $code);
|
||||
$localeAttempts[$code] = $result === $code;
|
||||
}
|
||||
setlocale(LC_ALL, (string) $original);
|
||||
|
||||
return [
|
||||
'user_id' => auth()->user()->id,
|
||||
'user_count' => User::count(),
|
||||
'user_flags' => $userFlags,
|
||||
'user_agent' => $userAgent,
|
||||
'primary' => Amount::getPrimaryCurrency(),
|
||||
'convert_to_primary' => Amount::convertToPrimary(),
|
||||
'locale_attempts' => $localeAttempts,
|
||||
'locale' => Steam::getLocale(),
|
||||
'language' => Steam::getLanguage(),
|
||||
'view_range' => Preferences::get('viewRange', '1M')->data,
|
||||
];
|
||||
}
|
||||
|
||||
private function getUserFlags(): string
|
||||
{
|
||||
$flags = [];
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
// has liabilities
|
||||
if ($user->accounts()->accountTypeIn([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value])->count() > 0) {
|
||||
$flags[] = '<span title="Has liabilities">:credit_card:</span>';
|
||||
}
|
||||
|
||||
// has piggies
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUser($user);
|
||||
|
||||
if ($repository->getPiggyBanks()->count() > 0) {
|
||||
$flags[] = '<span title="Has piggy banks">:pig:</span>';
|
||||
}
|
||||
|
||||
// has stored reconciliations
|
||||
$type = TransactionType::whereType(TransactionTypeEnum::RECONCILIATION->value)->first();
|
||||
if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) {
|
||||
$flags[] = '<span title="Has reconciled">:ledger:</span>';
|
||||
}
|
||||
|
||||
// has used importer?
|
||||
|
||||
// has rules
|
||||
if ($user->rules()->count() > 0) {
|
||||
$flags[] = '<span title="Has rules">:wrench:</span>';
|
||||
}
|
||||
|
||||
// has recurring transactions
|
||||
if ($user->recurrences()->count() > 0) {
|
||||
$flags[] = '<span title="Has recurring transactions">:clock130:</span>';
|
||||
}
|
||||
|
||||
// has groups
|
||||
if ($user->objectGroups()->count() > 0) {
|
||||
$flags[] = '<span title="Has object groups">:bookmark_tabs:</span>';
|
||||
}
|
||||
|
||||
// uses bills
|
||||
if ($user->bills()->count() > 0) {
|
||||
$flags[] = '<span title="Has subscriptions">:email:</span>';
|
||||
}
|
||||
|
||||
return implode(' ', $flags);
|
||||
}
|
||||
|
||||
public function routes(Request $request): never
|
||||
{
|
||||
if (!auth()->user()->hasRole('owner')) {
|
||||
@@ -456,6 +267,100 @@ class DebugController extends Controller
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash all types of messages.
|
||||
*/
|
||||
public function testFlash(Request $request): Redirector|RedirectResponse
|
||||
{
|
||||
$request->session()->flash('success', 'This is a success message.');
|
||||
$request->session()->flash('info', 'This is an info message.');
|
||||
$request->session()->flash('warning', 'This is a warning.');
|
||||
$request->session()->flash('error', 'This is an error!');
|
||||
|
||||
return redirect(route('home'));
|
||||
}
|
||||
|
||||
private function generateTable(): string
|
||||
{
|
||||
// system information:
|
||||
$system = $this->getSystemInformation();
|
||||
$docker = $this->getBuildInfo();
|
||||
$app = $this->getAppInfo();
|
||||
$user = $this->getUserInfo();
|
||||
|
||||
return (string) view('partials.debug-table', ['system' => $system, 'docker' => $docker, 'app' => $app, 'user' => $user]);
|
||||
}
|
||||
|
||||
private function getAppInfo(): array
|
||||
{
|
||||
$userGuard = config('auth.defaults.guard');
|
||||
|
||||
$config = FireflyConfig::get('last_rt_job', 0);
|
||||
$lastTime = (int) $config->data;
|
||||
$lastCronjob = 'never';
|
||||
$lastCronjobAgo = 'never';
|
||||
if ($lastTime > 0) {
|
||||
$carbon = Carbon::createFromTimestamp($lastTime);
|
||||
$lastCronjob = $carbon->format('Y-m-d H:i:s');
|
||||
$lastCronjobAgo = $carbon->locale('en')->diffForHumans(); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
return [
|
||||
'debug' => var_export(config('app.debug'), true),
|
||||
'audit_log_channel' => envNonEmpty('AUDIT_LOG_CHANNEL', '(empty)'),
|
||||
'default_language' => (string) config('firefly.default_language'),
|
||||
'default_locale' => (string) config('firefly.default_locale'),
|
||||
'remote_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_header') : 'N/A',
|
||||
'remote_mail_header' => 'remote_user_guard' === $userGuard ? config('auth.guard_email') : 'N/A',
|
||||
'stateful_domains' => implode(', ', config('sanctum.stateful')),
|
||||
|
||||
// the dates for the cron job are based on the recurring cron job's times.
|
||||
// any of the cron jobs will do, they always run at the same time.
|
||||
// but this job is the oldest, so the biggest chance it ran once
|
||||
|
||||
'last_cronjob' => $lastCronjob,
|
||||
'last_cronjob_ago' => $lastCronjobAgo,
|
||||
];
|
||||
}
|
||||
|
||||
private function getBuildInfo(): array
|
||||
{
|
||||
$return = [
|
||||
'is_docker' => env('IS_DOCKER', false), // @phpstan-ignore-line
|
||||
'build' => '(unknown)',
|
||||
'build_date' => '(unknown)',
|
||||
'base_build' => '(unknown)',
|
||||
'base_build_date' => '(unknown)',
|
||||
];
|
||||
|
||||
try {
|
||||
if (file_exists('/var/www/counter-main.txt')) {
|
||||
$return['build'] = trim(file_get_contents('/var/www/counter-main.txt'));
|
||||
Log::debug(sprintf('build is now "%s"', $return['build']));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::debug('Could not check build counter, but thats ok.');
|
||||
Log::warning($e->getMessage());
|
||||
}
|
||||
|
||||
try {
|
||||
if (file_exists('/var/www/build-date-main.txt')) {
|
||||
$return['build_date'] = trim(file_get_contents('/var/www/build-date-main.txt'));
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
Log::debug('Could not check build date, but thats ok.');
|
||||
Log::warning($e->getMessage());
|
||||
}
|
||||
if ('' !== (string) env('BASE_IMAGE_BUILD')) { // @phpstan-ignore-line
|
||||
$return['base_build'] = env('BASE_IMAGE_BUILD'); // @phpstan-ignore-line
|
||||
}
|
||||
if ('' !== (string) env('BASE_IMAGE_DATE')) { // @phpstan-ignore-line
|
||||
$return['base_build_date'] = env('BASE_IMAGE_DATE'); // @phpstan-ignore-line
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getParameter(string $name): string
|
||||
{
|
||||
switch ($name) {
|
||||
@@ -552,16 +457,111 @@ class DebugController extends Controller
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flash all types of messages.
|
||||
*/
|
||||
public function testFlash(Request $request): Redirector|RedirectResponse
|
||||
private function getSystemInformation(): array
|
||||
{
|
||||
$request->session()->flash('success', 'This is a success message.');
|
||||
$request->session()->flash('info', 'This is an info message.');
|
||||
$request->session()->flash('warning', 'This is a warning.');
|
||||
$request->session()->flash('error', 'This is an error!');
|
||||
$maxFileSize = Steam::phpBytes(ini_get('upload_max_filesize'));
|
||||
$maxPostSize = Steam::phpBytes(ini_get('post_max_size'));
|
||||
$drivers = DB::availableDrivers();
|
||||
$currentDriver = DB::getDriverName();
|
||||
|
||||
return redirect(route('home'));
|
||||
return [
|
||||
'php_version' => PHP_VERSION,
|
||||
'php_os' => PHP_OS,
|
||||
'build_time' => config('firefly.build_time'),
|
||||
'build_time_nice' => Carbon::parse(config('firefly.build_time'), 'Europe/Amsterdam')->setTimezone('Europe/Amsterdam')->format('Y-m-d H:i:s e'),
|
||||
'uname' => php_uname('m'),
|
||||
'interface' => PHP_SAPI,
|
||||
'bits' => PHP_INT_SIZE * 8,
|
||||
'bcscale' => bcscale(),
|
||||
'display_errors' => ini_get('display_errors'),
|
||||
'error_reporting' => $this->errorReporting((int) ini_get('error_reporting')),
|
||||
'upload_size' => min($maxFileSize, $maxPostSize),
|
||||
'all_drivers' => $drivers,
|
||||
'current_driver' => $currentDriver,
|
||||
];
|
||||
}
|
||||
|
||||
private function getUserFlags(): string
|
||||
{
|
||||
$flags = [];
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
// has liabilities
|
||||
if ($user->accounts()->accountTypeIn([AccountTypeEnum::DEBT->value, AccountTypeEnum::LOAN->value, AccountTypeEnum::MORTGAGE->value])->count() > 0) {
|
||||
$flags[] = '<span title="Has liabilities">:credit_card:</span>';
|
||||
}
|
||||
|
||||
// has piggies
|
||||
$repository = app(PiggyBankRepositoryInterface::class);
|
||||
$repository->setUser($user);
|
||||
|
||||
if ($repository->getPiggyBanks()->count() > 0) {
|
||||
$flags[] = '<span title="Has piggy banks">:pig:</span>';
|
||||
}
|
||||
|
||||
// has stored reconciliations
|
||||
$type = TransactionType::whereType(TransactionTypeEnum::RECONCILIATION->value)->first();
|
||||
if ($user->transactionJournals()->where('transaction_type_id', $type->id)->count() > 0) {
|
||||
$flags[] = '<span title="Has reconciled">:ledger:</span>';
|
||||
}
|
||||
|
||||
// has used importer?
|
||||
|
||||
// has rules
|
||||
if ($user->rules()->count() > 0) {
|
||||
$flags[] = '<span title="Has rules">:wrench:</span>';
|
||||
}
|
||||
|
||||
// has recurring transactions
|
||||
if ($user->recurrences()->count() > 0) {
|
||||
$flags[] = '<span title="Has recurring transactions">:clock130:</span>';
|
||||
}
|
||||
|
||||
// has groups
|
||||
if ($user->objectGroups()->count() > 0) {
|
||||
$flags[] = '<span title="Has object groups">:bookmark_tabs:</span>';
|
||||
}
|
||||
|
||||
// uses bills
|
||||
if ($user->bills()->count() > 0) {
|
||||
$flags[] = '<span title="Has subscriptions">:email:</span>';
|
||||
}
|
||||
|
||||
return implode(' ', $flags);
|
||||
}
|
||||
|
||||
private function getUserInfo(): array
|
||||
{
|
||||
$userFlags = $this->getUserFlags();
|
||||
|
||||
// user info
|
||||
$userAgent = request()->header('user-agent');
|
||||
|
||||
// set languages, see what happens:
|
||||
$original = setlocale(LC_ALL, '0');
|
||||
$localeAttempts = [];
|
||||
$parts = Steam::getLocaleArray(Steam::getLocale());
|
||||
foreach ($parts as $code) {
|
||||
$code = trim($code);
|
||||
Log::debug(sprintf('Trying to set %s', $code));
|
||||
$result = setlocale(LC_ALL, $code);
|
||||
$localeAttempts[$code] = $result === $code;
|
||||
}
|
||||
setlocale(LC_ALL, (string) $original);
|
||||
|
||||
return [
|
||||
'user_id' => auth()->user()->id,
|
||||
'user_count' => User::count(),
|
||||
'user_flags' => $userFlags,
|
||||
'user_agent' => $userAgent,
|
||||
'primary' => Amount::getPrimaryCurrency(),
|
||||
'convert_to_primary' => Amount::convertToPrimary(),
|
||||
'locale_attempts' => $localeAttempts,
|
||||
'locale' => Steam::getLocale(),
|
||||
'language' => Steam::getLanguage(),
|
||||
'view_range' => Preferences::get('viewRange', '1M')->data,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,37 +162,6 @@ class ReconcileController extends Controller
|
||||
return response()->json($return);
|
||||
}
|
||||
|
||||
private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string
|
||||
{
|
||||
$toAdd = '0';
|
||||
Log::debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description']));
|
||||
|
||||
// not much magic below we need to cover using tests.
|
||||
|
||||
if ($account->id === $journal['source_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = $journal['amount'];
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = $journal['foreign_amount'];
|
||||
}
|
||||
}
|
||||
if ($account->id === $journal['destination_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = bcmul((string) $journal['amount'], '-1');
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = bcmul((string) $journal['foreign_amount'], '-1');
|
||||
}
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Going to add %s to %s', $toAdd, $amount));
|
||||
$amount = bcadd($amount, (string) $toAdd);
|
||||
Log::debug(sprintf('Result is %s', $amount));
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of transactions in a modal.
|
||||
*
|
||||
@@ -278,6 +247,37 @@ class ReconcileController extends Controller
|
||||
return response()->json(['html' => $html, 'startBalance' => $startBalance, 'endBalance' => $endBalance]);
|
||||
}
|
||||
|
||||
private function processJournal(Account $account, TransactionCurrency $currency, array $journal, string $amount): string
|
||||
{
|
||||
$toAdd = '0';
|
||||
Log::debug(sprintf('User submitted %s #%d: "%s"', $journal['transaction_type_type'], $journal['transaction_journal_id'], $journal['description']));
|
||||
|
||||
// not much magic below we need to cover using tests.
|
||||
|
||||
if ($account->id === $journal['source_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = $journal['amount'];
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = $journal['foreign_amount'];
|
||||
}
|
||||
}
|
||||
if ($account->id === $journal['destination_account_id']) {
|
||||
if ($currency->id === $journal['currency_id']) {
|
||||
$toAdd = bcmul((string) $journal['amount'], '-1');
|
||||
}
|
||||
if (null !== $journal['foreign_currency_id'] && $journal['foreign_currency_id'] === $currency->id) {
|
||||
$toAdd = bcmul((string) $journal['foreign_amount'], '-1');
|
||||
}
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Going to add %s to %s', $toAdd, $amount));
|
||||
$amount = bcadd($amount, (string) $toAdd);
|
||||
Log::debug(sprintf('Result is %s', $amount));
|
||||
|
||||
return $amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* "fix" amounts to make it easier on the reconciliation overview:
|
||||
*/
|
||||
|
||||
@@ -63,14 +63,6 @@ class EditController extends Controller
|
||||
});
|
||||
}
|
||||
|
||||
public function resetHistory(PiggyBank $piggyBank): RedirectResponse
|
||||
{
|
||||
$this->piggyRepos->resetHistory($piggyBank);
|
||||
session()->flash('success', (string) trans('firefly.piggy_history_reset'));
|
||||
|
||||
return redirect(route('piggy-banks.show', [$piggyBank->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a piggy bank.
|
||||
*
|
||||
@@ -117,6 +109,14 @@ class EditController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
public function resetHistory(PiggyBank $piggyBank): RedirectResponse
|
||||
{
|
||||
$this->piggyRepos->resetHistory($piggyBank);
|
||||
session()->flash('success', (string) trans('firefly.piggy_history_reset'));
|
||||
|
||||
return redirect(route('piggy-banks.show', [$piggyBank->id]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a piggy bank.
|
||||
*/
|
||||
|
||||
@@ -102,36 +102,22 @@ class IndexController extends Controller
|
||||
return view('piggy-banks.index', ['piggyBanks' => $piggyBanks, 'accounts' => $accounts]);
|
||||
}
|
||||
|
||||
private function groupPiggyBanks(Collection $collection): array
|
||||
/**
|
||||
* Set the order of a piggy bank.
|
||||
*/
|
||||
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
|
||||
{
|
||||
/** @var PiggyBankTransformer $transformer */
|
||||
$transformer = app(PiggyBankTransformer::class);
|
||||
$transformer->setParameters(new ParameterBag());
|
||||
$piggyBanks = [];
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new PiggyBankEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$collection = $enrichment->enrich($collection);
|
||||
|
||||
/** @var PiggyBank $piggy */
|
||||
foreach ($collection as $piggy) {
|
||||
$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);
|
||||
|
||||
// sum the total amount for the index.
|
||||
$piggyBanks[$groupOrder]['piggy_banks'][] = $array;
|
||||
$objectGroupTitle = (string) $request->get('objectGroupTitle');
|
||||
$newOrder = (int) $request->get('order');
|
||||
$this->piggyRepos->setOrder($piggyBank, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->piggyRepos->removeObjectGroup($piggyBank);
|
||||
}
|
||||
|
||||
return $piggyBanks;
|
||||
return response()->json(['data' => 'OK']);
|
||||
}
|
||||
|
||||
private function collectAccounts(Collection $collection): array
|
||||
@@ -178,30 +164,36 @@ class IndexController extends Controller
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function mergeAccountsAndPiggies(array $piggyBanks, array $accounts): array
|
||||
private function groupPiggyBanks(Collection $collection): array
|
||||
{
|
||||
/** @var array $group */
|
||||
foreach ($piggyBanks as $group) {
|
||||
/** @var array $piggyBank */
|
||||
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.
|
||||
/** @var array $piggyAccount */
|
||||
foreach ($piggyBank['accounts'] as $piggyAccount) {
|
||||
$accountId = $piggyAccount['account_id'];
|
||||
if (array_key_exists($accountId, $accounts)) {
|
||||
$accounts[$accountId]['left'] = bcsub((string) $accounts[$accountId]['left'], (string) $piggyAccount['current_amount']);
|
||||
$accounts[$accountId]['saved'] = bcadd((string) $accounts[$accountId]['saved'], (string) $piggyAccount['current_amount']);
|
||||
$accounts[$accountId]['target'] = bcadd((string) $accounts[$accountId]['target'], (string) $piggyBank['target_amount']);
|
||||
$accounts[$accountId]['to_save'] = bcadd((string) $accounts[$accountId]['to_save'], bcsub(
|
||||
(string) $piggyBank['target_amount'],
|
||||
(string) $piggyAccount['current_amount']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
/** @var PiggyBankTransformer $transformer */
|
||||
$transformer = app(PiggyBankTransformer::class);
|
||||
$transformer->setParameters(new ParameterBag());
|
||||
$piggyBanks = [];
|
||||
|
||||
// enrich
|
||||
/** @var User $admin */
|
||||
$admin = auth()->user();
|
||||
$enrichment = new PiggyBankEnrichment();
|
||||
$enrichment->setUser($admin);
|
||||
$collection = $enrichment->enrich($collection);
|
||||
|
||||
/** @var PiggyBank $piggy */
|
||||
foreach ($collection as $piggy) {
|
||||
$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);
|
||||
|
||||
// sum the total amount for the index.
|
||||
$piggyBanks[$groupOrder]['piggy_banks'][] = $array;
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
return $piggyBanks;
|
||||
}
|
||||
|
||||
private function makeSums(array $piggyBanks): array
|
||||
@@ -239,21 +231,29 @@ class IndexController extends Controller
|
||||
return $piggyBanks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the order of a piggy bank.
|
||||
*/
|
||||
public function setOrder(Request $request, PiggyBank $piggyBank): JsonResponse
|
||||
private function mergeAccountsAndPiggies(array $piggyBanks, array $accounts): array
|
||||
{
|
||||
$objectGroupTitle = (string) $request->get('objectGroupTitle');
|
||||
$newOrder = (int) $request->get('order');
|
||||
$this->piggyRepos->setOrder($piggyBank, $newOrder);
|
||||
if ('' !== $objectGroupTitle) {
|
||||
$this->piggyRepos->setObjectGroup($piggyBank, $objectGroupTitle);
|
||||
}
|
||||
if ('' === $objectGroupTitle) {
|
||||
$this->piggyRepos->removeObjectGroup($piggyBank);
|
||||
/** @var array $group */
|
||||
foreach ($piggyBanks as $group) {
|
||||
/** @var array $piggyBank */
|
||||
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.
|
||||
/** @var array $piggyAccount */
|
||||
foreach ($piggyBank['accounts'] as $piggyAccount) {
|
||||
$accountId = $piggyAccount['account_id'];
|
||||
if (array_key_exists($accountId, $accounts)) {
|
||||
$accounts[$accountId]['left'] = bcsub((string) $accounts[$accountId]['left'], (string) $piggyAccount['current_amount']);
|
||||
$accounts[$accountId]['saved'] = bcadd((string) $accounts[$accountId]['saved'], (string) $piggyAccount['current_amount']);
|
||||
$accounts[$accountId]['target'] = bcadd((string) $accounts[$accountId]['target'], (string) $piggyBank['target_amount']);
|
||||
$accounts[$accountId]['to_save'] = bcadd((string) $accounts[$accountId]['to_save'], bcsub(
|
||||
(string) $piggyBank['target_amount'],
|
||||
(string) $piggyAccount['current_amount']
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response()->json(['data' => 'OK']);
|
||||
return $accounts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +274,21 @@ class MfaController extends Controller
|
||||
return redirect(route('profile.mfa.backup-codes'));
|
||||
}
|
||||
|
||||
public function index(): Factory|RedirectResponse|View
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
request()->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$subTitle = (string) trans('firefly.mfa_index_title');
|
||||
$subTitleIcon = 'fa-calculator';
|
||||
$enabledMFA = null !== auth()->user()->mfa_secret;
|
||||
|
||||
return view('profile.mfa.index')->with(['subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon, 'enabledMFA' => $enabledMFA]);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO duplicate code.
|
||||
*
|
||||
@@ -309,19 +324,4 @@ class MfaController extends Controller
|
||||
}
|
||||
Preferences::set('mfa_history', $newHistory);
|
||||
}
|
||||
|
||||
public function index(): Factory|RedirectResponse|View
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
request()->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$subTitle = (string) trans('firefly.mfa_index_title');
|
||||
$subTitleIcon = 'fa-calculator';
|
||||
$enabledMFA = null !== auth()->user()->mfa_secret;
|
||||
|
||||
return view('profile.mfa.index')->with(['subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon, 'enabledMFA' => $enabledMFA]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +85,45 @@ class ProfileController extends Controller
|
||||
$this->middleware(IsDemoUser::class)->except(['index']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change your email address.
|
||||
*/
|
||||
public function changeEmail(Request $request): Factory|RedirectResponse|View
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
$request->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$title = auth()->user()->email;
|
||||
$email = auth()->user()->email;
|
||||
$subTitle = (string) trans('firefly.change_your_email');
|
||||
$subTitleIcon = 'fa-envelope';
|
||||
|
||||
return view('profile.change-email', ['title' => $title, 'subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon, 'email' => $email]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change your password.
|
||||
*
|
||||
* @return Factory|Redirector|RedirectResponse|View
|
||||
*/
|
||||
public function changePassword(Request $request): Factory|\Illuminate\Contracts\View\View|Redirector|RedirectResponse
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
$request->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$title = auth()->user()->email;
|
||||
$subTitle = (string) trans('firefly.change_your_password');
|
||||
$subTitleIcon = 'fa-key';
|
||||
|
||||
return view('profile.change-password', ['title' => $title, 'subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Screen to confirm email change.
|
||||
*
|
||||
@@ -242,25 +281,6 @@ class ProfileController extends Controller
|
||||
return redirect(route('index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change your email address.
|
||||
*/
|
||||
public function changeEmail(Request $request): Factory|RedirectResponse|View
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
$request->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$title = auth()->user()->email;
|
||||
$email = auth()->user()->email;
|
||||
$subTitle = (string) trans('firefly.change_your_email');
|
||||
$subTitleIcon = 'fa-envelope';
|
||||
|
||||
return view('profile.change-email', ['title' => $title, 'subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon, 'email' => $email]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit change password form.
|
||||
*/
|
||||
@@ -293,26 +313,6 @@ class ProfileController extends Controller
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change your password.
|
||||
*
|
||||
* @return Factory|Redirector|RedirectResponse|View
|
||||
*/
|
||||
public function changePassword(Request $request): Factory|\Illuminate\Contracts\View\View|Redirector|RedirectResponse
|
||||
{
|
||||
if (!$this->internalAuth) {
|
||||
$request->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('profile.index'));
|
||||
}
|
||||
|
||||
$title = auth()->user()->email;
|
||||
$subTitle = (string) trans('firefly.change_your_password');
|
||||
$subTitleIcon = 'fa-key';
|
||||
|
||||
return view('profile.change-password', ['title' => $title, 'subTitle' => $subTitle, 'subTitleIcon' => $subTitleIcon]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit delete account.
|
||||
*/
|
||||
|
||||
@@ -278,24 +278,6 @@ class DoubleController extends Controller
|
||||
return view('reports.double.partials.accounts', ['sums' => $sums, 'report' => $report]);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this method is duplicated.
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Factory|View
|
||||
*/
|
||||
@@ -486,4 +468,22 @@ class DoubleController extends Controller
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO this method is duplicated.
|
||||
*/
|
||||
private function getCounterpartName(Collection $accounts, int $id, string $name, ?string $iban): string
|
||||
{
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
if ($account->name === $name && $account->id !== $id) {
|
||||
return $account->name;
|
||||
}
|
||||
if (null !== $account->iban && $account->iban === $iban && $account->id !== $id) {
|
||||
return $account->iban;
|
||||
}
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,6 +151,34 @@ class EditController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rule.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function update(RuleFormRequest $request, Rule $rule)
|
||||
{
|
||||
$data = $request->getRuleData();
|
||||
|
||||
$this->ruleRepos->update($rule, $data);
|
||||
|
||||
session()->flash('success', (string) trans('firefly.updated_rule', ['title' => $rule->title]));
|
||||
Preferences::mark();
|
||||
$redirect = redirect($this->getPreviousUrl('rules.edit.url'));
|
||||
|
||||
if (true === $data['run_after_form']) {
|
||||
return redirect(route('rules.select-transactions', [$rule->id]));
|
||||
}
|
||||
|
||||
if (1 === (int) $request->get('return_to_edit')) {
|
||||
session()->put('rules.edit.fromUpdate', true);
|
||||
|
||||
$redirect = redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
@@ -189,32 +217,4 @@ class EditController extends Controller
|
||||
|
||||
return $renderedEntries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rule.
|
||||
*
|
||||
* @return Redirector|RedirectResponse
|
||||
*/
|
||||
public function update(RuleFormRequest $request, Rule $rule)
|
||||
{
|
||||
$data = $request->getRuleData();
|
||||
|
||||
$this->ruleRepos->update($rule, $data);
|
||||
|
||||
session()->flash('success', (string) trans('firefly.updated_rule', ['title' => $rule->title]));
|
||||
Preferences::mark();
|
||||
$redirect = redirect($this->getPreviousUrl('rules.edit.url'));
|
||||
|
||||
if (true === $data['run_after_form']) {
|
||||
return redirect(route('rules.select-transactions', [$rule->id]));
|
||||
}
|
||||
|
||||
if (1 === (int) $request->get('return_to_edit')) {
|
||||
session()->put('rules.edit.fromUpdate', true);
|
||||
|
||||
$redirect = redirect(route('rules.edit', [$rule->id]))->withInput(['return_to_edit' => 1]);
|
||||
}
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,23 @@ class InstallController extends Controller
|
||||
return view('install.index');
|
||||
}
|
||||
|
||||
/**
|
||||
* Create specific RSA keys.
|
||||
*/
|
||||
public function keys(): void
|
||||
{
|
||||
$key = RSA::createKey(4096);
|
||||
|
||||
[$publicKey, $privateKey] = [Passport::keyPath('oauth-public.key'), Passport::keyPath('oauth-private.key')];
|
||||
|
||||
if (file_exists($publicKey) || file_exists($privateKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents($publicKey, (string) $key->getPublicKey());
|
||||
file_put_contents($privateKey, $key->toString('PKCS1'));
|
||||
}
|
||||
|
||||
public function runCommand(Request $request): JsonResponse
|
||||
{
|
||||
$requestIndex = (int) $request->get('index');
|
||||
@@ -149,21 +166,4 @@ class InstallController extends Controller
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create specific RSA keys.
|
||||
*/
|
||||
public function keys(): void
|
||||
{
|
||||
$key = RSA::createKey(4096);
|
||||
|
||||
[$publicKey, $privateKey] = [Passport::keyPath('oauth-public.key'), Passport::keyPath('oauth-private.key')];
|
||||
|
||||
if (file_exists($publicKey) || file_exists($privateKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_put_contents($publicKey, (string) $key->getPublicKey());
|
||||
file_put_contents($privateKey, $key->toString('PKCS1'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,6 +113,20 @@ class TagController extends Controller
|
||||
return view('tags.delete', ['tag' => $tag, 'subTitle' => $subTitle]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a tag.
|
||||
*/
|
||||
public function destroy(Tag $tag): RedirectResponse
|
||||
{
|
||||
$tagName = $tag->tag;
|
||||
$this->repository->destroy($tag);
|
||||
|
||||
session()->flash('success', (string) trans('firefly.deleted_tag', ['tag' => $tagName]));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect($this->getPreviousUrl('tags.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a tag.
|
||||
*
|
||||
@@ -198,20 +212,6 @@ class TagController extends Controller
|
||||
return redirect(route('tags.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a tag.
|
||||
*/
|
||||
public function destroy(Tag $tag): RedirectResponse
|
||||
{
|
||||
$tagName = $tag->tag;
|
||||
$this->repository->destroy($tag);
|
||||
|
||||
session()->flash('success', (string) trans('firefly.deleted_tag', ['tag' => $tagName]));
|
||||
Preferences::mark();
|
||||
|
||||
return redirect($this->getPreviousUrl('tags.delete.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a single tag.
|
||||
*
|
||||
|
||||
@@ -129,7 +129,6 @@ class BulkController extends Controller
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
|
||||
|
||||
Preferences::mark();
|
||||
$request->session()->flash('success', trans_choice('firefly.mass_edited_transactions_success', $count));
|
||||
|
||||
@@ -148,6 +147,17 @@ class BulkController extends Controller
|
||||
return true;
|
||||
}
|
||||
|
||||
private function updateJournalCategory(TransactionJournal $journal, bool $ignoreUpdate, string $category): bool
|
||||
{
|
||||
if ($ignoreUpdate) {
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Set budget to %s', $category));
|
||||
$this->repository->updateCategory($journal, $category);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function updateJournalTags(TransactionJournal $journal, string $action, array $tags): bool
|
||||
{
|
||||
if ('do_replace' === $action) {
|
||||
@@ -162,15 +172,4 @@ class BulkController extends Controller
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function updateJournalCategory(TransactionJournal $journal, bool $ignoreUpdate, string $category): bool
|
||||
{
|
||||
if ($ignoreUpdate) {
|
||||
return false;
|
||||
}
|
||||
Log::debug(sprintf('Set budget to %s', $category));
|
||||
$this->repository->updateCategory($journal, $category);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,149 +138,6 @@ class ConvertController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function getValidDepositSources(): array
|
||||
{
|
||||
// make repositories
|
||||
$liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value];
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([
|
||||
AccountTypeEnum::REVENUE->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$name = $account->name;
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
// maybe it's a liability thing:
|
||||
if (in_array($account->accountType->type, $liabilityTypes, true)) {
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
}
|
||||
if (AccountTypeEnum::CASH->value === $account->accountType->type) {
|
||||
$role = 'cash_account';
|
||||
$name = sprintf('(%s)', trans('firefly.cash'));
|
||||
}
|
||||
if (AccountTypeEnum::REVENUE->value === $account->accountType->type) {
|
||||
$role = 'revenue_account';
|
||||
}
|
||||
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $name;
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
private function getValidWithdrawalDests(): array
|
||||
{
|
||||
// make repositories
|
||||
$liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value];
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([
|
||||
AccountTypeEnum::EXPENSE->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$name = $account->name;
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
// maybe it's a liability thing:
|
||||
if (in_array($account->accountType->type, $liabilityTypes, true)) {
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
}
|
||||
if (AccountTypeEnum::CASH->value === $account->accountType->type) {
|
||||
$role = 'cash_account';
|
||||
$name = sprintf('(%s)', trans('firefly.cash'));
|
||||
}
|
||||
if (AccountTypeEnum::EXPENSE->value === $account->accountType->type) {
|
||||
$role = 'expense_account';
|
||||
}
|
||||
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $name;
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getLiabilities(): array
|
||||
{
|
||||
// make repositories
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$date = today()->endOfDay();
|
||||
Log::debug(sprintf('getLiabilities: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0';
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
$role = sprintf('l_%s', $account->accountType->type);
|
||||
$key = (string) trans(sprintf('firefly.opt_group_%s', $role));
|
||||
$grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false));
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getAssetAccounts(): array
|
||||
{
|
||||
// make repositories
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$date = today()->endOfDay();
|
||||
Log::debug(sprintf('getAssetAccounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0';
|
||||
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
$key = (string) trans(sprintf('firefly.opt_group_%s', $role));
|
||||
$grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false));
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* Do the conversion.
|
||||
*
|
||||
@@ -313,7 +170,6 @@ class ConvertController extends Controller
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
|
||||
|
||||
return redirect(route('transactions.show', [$group->id]));
|
||||
}
|
||||
|
||||
@@ -404,4 +260,147 @@ class ConvertController extends Controller
|
||||
|
||||
return $journal;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getAssetAccounts(): array
|
||||
{
|
||||
// make repositories
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([AccountTypeEnum::ASSET->value]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$date = today()->endOfDay();
|
||||
Log::debug(sprintf('getAssetAccounts: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0';
|
||||
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
$key = (string) trans(sprintf('firefly.opt_group_%s', $role));
|
||||
$grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false));
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
private function getLiabilities(): array
|
||||
{
|
||||
// make repositories
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$date = today()->endOfDay();
|
||||
Log::debug(sprintf('getLiabilities: Call finalAccountBalance with date/time "%s"', $date->toIso8601String()));
|
||||
// 2025-10-08 replace finalAccountBalance with accountsBalancesOptimized.
|
||||
// $balance = Steam::finalAccountBalance($account, $date)['balance'];
|
||||
$balance = Steam::accountsBalancesOptimized(new Collection()->push($account), $date)[$account->id]['balance'] ?? '0';
|
||||
$currency = $this->accountRepository->getAccountCurrency($account) ?? $this->primaryCurrency;
|
||||
$role = sprintf('l_%s', $account->accountType->type);
|
||||
$key = (string) trans(sprintf('firefly.opt_group_%s', $role));
|
||||
$grouped[$key][$account->id] = sprintf('%s (%s)', $account->name, Amount::formatAnything($currency, $balance, false));
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
private function getValidDepositSources(): array
|
||||
{
|
||||
// make repositories
|
||||
$liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value];
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([
|
||||
AccountTypeEnum::REVENUE->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$name = $account->name;
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
// maybe it's a liability thing:
|
||||
if (in_array($account->accountType->type, $liabilityTypes, true)) {
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
}
|
||||
if (AccountTypeEnum::CASH->value === $account->accountType->type) {
|
||||
$role = 'cash_account';
|
||||
$name = sprintf('(%s)', trans('firefly.cash'));
|
||||
}
|
||||
if (AccountTypeEnum::REVENUE->value === $account->accountType->type) {
|
||||
$role = 'revenue_account';
|
||||
}
|
||||
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $name;
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
|
||||
private function getValidWithdrawalDests(): array
|
||||
{
|
||||
// make repositories
|
||||
$liabilityTypes = [AccountTypeEnum::MORTGAGE->value, AccountTypeEnum::DEBT->value, AccountTypeEnum::CREDITCARD->value, AccountTypeEnum::LOAN->value];
|
||||
$accountList = $this->accountRepository->getActiveAccountsByType([
|
||||
AccountTypeEnum::EXPENSE->value,
|
||||
AccountTypeEnum::CASH->value,
|
||||
AccountTypeEnum::LOAN->value,
|
||||
AccountTypeEnum::DEBT->value,
|
||||
AccountTypeEnum::MORTGAGE->value,
|
||||
]);
|
||||
$grouped = [];
|
||||
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
|
||||
$name = $account->name;
|
||||
if ('' === $role) {
|
||||
$role = 'no_account_type';
|
||||
}
|
||||
|
||||
// maybe it's a liability thing:
|
||||
if (in_array($account->accountType->type, $liabilityTypes, true)) {
|
||||
$role = 'l_'.$account->accountType->type;
|
||||
}
|
||||
if (AccountTypeEnum::CASH->value === $account->accountType->type) {
|
||||
$role = 'cash_account';
|
||||
$name = sprintf('(%s)', trans('firefly.cash'));
|
||||
}
|
||||
if (AccountTypeEnum::EXPENSE->value === $account->accountType->type) {
|
||||
$role = 'expense_account';
|
||||
}
|
||||
|
||||
$key = (string) trans('firefly.opt_group_'.$role);
|
||||
$grouped[$key][$account->id] = $name;
|
||||
}
|
||||
|
||||
return $grouped;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class DeleteController extends Controller
|
||||
|
||||
// translations:
|
||||
$this->middleware(function ($request, $next) {
|
||||
app('view')->share('title', (string)trans('firefly.transactions'));
|
||||
app('view')->share('title', (string) trans('firefly.transactions'));
|
||||
app('view')->share('mainTitleIcon', 'fa-exchange');
|
||||
|
||||
$this->repository = app(TransactionGroupRepositoryInterface::class);
|
||||
@@ -64,7 +64,7 @@ class DeleteController extends Controller
|
||||
/**
|
||||
* Shows the form that allows a user to delete a transaction journal.
|
||||
*/
|
||||
public function delete(TransactionGroup $group): Factory | Redirector | RedirectResponse | View
|
||||
public function delete(TransactionGroup $group): Factory|Redirector|RedirectResponse|View
|
||||
{
|
||||
if (!$this->isEditableGroup($group)) {
|
||||
return $this->redirectGroupToAccount($group);
|
||||
@@ -72,12 +72,12 @@ class DeleteController extends Controller
|
||||
|
||||
Log::debug(sprintf('Start of delete view for group #%d', $group->id));
|
||||
|
||||
$journal = $group->transactionJournals->first();
|
||||
$journal = $group->transactionJournals->first();
|
||||
if (null === $journal) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
$subTitle = (string)trans('firefly.delete_' . $objectType, ['description' => $group->title ?? $journal->description]);
|
||||
$subTitle = (string) trans('firefly.delete_'.$objectType, ['description' => $group->title ?? $journal->description]);
|
||||
$previous = Steam::getSafePreviousUrl();
|
||||
// put previous url in session
|
||||
Log::debug('Will try to remember previous URL');
|
||||
@@ -95,19 +95,19 @@ class DeleteController extends Controller
|
||||
/**
|
||||
* Actually destroys the journal.
|
||||
*/
|
||||
public function destroy(TransactionGroup $group): Redirector | RedirectResponse
|
||||
public function destroy(TransactionGroup $group): Redirector|RedirectResponse
|
||||
{
|
||||
Log::debug(sprintf('Now in %s(#%d).', __METHOD__, $group->id));
|
||||
if (!$this->isEditableGroup($group)) {
|
||||
return $this->redirectGroupToAccount($group);
|
||||
}
|
||||
|
||||
$journal = $group->transactionJournals->first();
|
||||
$journal = $group->transactionJournals->first();
|
||||
if (null === $journal) {
|
||||
throw new NotFoundHttpException();
|
||||
}
|
||||
$objectType = strtolower($journal->transaction_type_type ?? $journal->transactionType->type);
|
||||
session()->flash('success', (string)trans('firefly.deleted_' . strtolower($objectType), ['description' => $group->title ?? $journal->description]));
|
||||
session()->flash('success', (string) trans('firefly.deleted_'.strtolower($objectType), ['description' => $group->title ?? $journal->description]));
|
||||
$this->repository->destroy($group);
|
||||
Preferences::mark();
|
||||
|
||||
|
||||
@@ -192,43 +192,6 @@ class MassController extends Controller
|
||||
return redirect($this->getPreviousUrl('transactions.mass-edit.url'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateJournal(int $journalId, MassEditJournalRequest $request): void
|
||||
{
|
||||
$journal = $this->repository->find($journalId);
|
||||
if (!$journal instanceof TransactionJournal) {
|
||||
throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId));
|
||||
}
|
||||
$service = app(JournalUpdateService::class);
|
||||
// for each field, call the update service.
|
||||
$service->setTransactionJournal($journal);
|
||||
|
||||
$data = [
|
||||
'date' => $this->getDateFromRequest($request, $journal->id, 'date'),
|
||||
'description' => $this->getStringFromRequest($request, $journal->id, 'description'),
|
||||
'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'),
|
||||
'source_name' => $this->getStringFromRequest($request, $journal->id, 'source_name'),
|
||||
'destination_id' => $this->getIntFromRequest($request, $journal->id, 'destination_id'),
|
||||
'destination_name' => $this->getStringFromRequest($request, $journal->id, 'destination_name'),
|
||||
'budget_id' => $this->getIntFromRequest($request, $journal->id, 'budget_id'),
|
||||
'category_name' => $this->getStringFromRequest($request, $journal->id, 'category'),
|
||||
'amount' => $this->getStringFromRequest($request, $journal->id, 'amount'),
|
||||
'foreign_amount' => $this->getStringFromRequest($request, $journal->id, 'foreign_amount'),
|
||||
];
|
||||
Log::debug(sprintf('Will update journal #%d with data.', $journal->id), $data);
|
||||
|
||||
// call service to update.
|
||||
$service->setData($data);
|
||||
$service->update();
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($journal->transactionGroup);
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
|
||||
}
|
||||
|
||||
private function getDateFromRequest(MassEditJournalRequest $request, int $journalId, string $key): ?Carbon
|
||||
{
|
||||
$value = $request->get($key);
|
||||
@@ -251,6 +214,19 @@ class MassController extends Controller
|
||||
return $carbon;
|
||||
}
|
||||
|
||||
private function getIntFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?int
|
||||
{
|
||||
$value = $request->get($string);
|
||||
if (!is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
if (!array_key_exists($journalId, $value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (int) $value[$journalId];
|
||||
}
|
||||
|
||||
private function getStringFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?string
|
||||
{
|
||||
$value = $request->get($string);
|
||||
@@ -264,16 +240,39 @@ class MassController extends Controller
|
||||
return (string) $value[$journalId];
|
||||
}
|
||||
|
||||
private function getIntFromRequest(MassEditJournalRequest $request, int $journalId, string $string): ?int
|
||||
/**
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function updateJournal(int $journalId, MassEditJournalRequest $request): void
|
||||
{
|
||||
$value = $request->get($string);
|
||||
if (!is_array($value)) {
|
||||
return null;
|
||||
}
|
||||
if (!array_key_exists($journalId, $value)) {
|
||||
return null;
|
||||
$journal = $this->repository->find($journalId);
|
||||
if (!$journal instanceof TransactionJournal) {
|
||||
throw new FireflyException(sprintf('Trying to edit non-existent or deleted journal #%d', $journalId));
|
||||
}
|
||||
$service = app(JournalUpdateService::class);
|
||||
// for each field, call the update service.
|
||||
$service->setTransactionJournal($journal);
|
||||
|
||||
return (int) $value[$journalId];
|
||||
$data = [
|
||||
'date' => $this->getDateFromRequest($request, $journal->id, 'date'),
|
||||
'description' => $this->getStringFromRequest($request, $journal->id, 'description'),
|
||||
'source_id' => $this->getIntFromRequest($request, $journal->id, 'source_id'),
|
||||
'source_name' => $this->getStringFromRequest($request, $journal->id, 'source_name'),
|
||||
'destination_id' => $this->getIntFromRequest($request, $journal->id, 'destination_id'),
|
||||
'destination_name' => $this->getStringFromRequest($request, $journal->id, 'destination_name'),
|
||||
'budget_id' => $this->getIntFromRequest($request, $journal->id, 'budget_id'),
|
||||
'category_name' => $this->getStringFromRequest($request, $journal->id, 'category'),
|
||||
'amount' => $this->getStringFromRequest($request, $journal->id, 'amount'),
|
||||
'foreign_amount' => $this->getStringFromRequest($request, $journal->id, 'foreign_amount'),
|
||||
];
|
||||
Log::debug(sprintf('Will update journal #%d with data.', $journal->id), $data);
|
||||
|
||||
// call service to update.
|
||||
$service->setData($data);
|
||||
$service->update();
|
||||
$flags = new TransactionGroupEventFlags();
|
||||
$objects = TransactionGroupEventObjects::collectFromTransactionGroup($journal->transactionGroup);
|
||||
event(new UpdatedSingleTransactionGroup($flags, $objects));
|
||||
event(new WebhookMessagesRequestSending());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,6 +163,31 @@ class ShowController extends Controller
|
||||
]);
|
||||
}
|
||||
|
||||
private function getAccounts(array $group): array
|
||||
{
|
||||
$accounts = ['source' => [], 'destination' => []];
|
||||
|
||||
foreach ($group['transactions'] as $transaction) {
|
||||
$accounts['source'][] = [
|
||||
'type' => $transaction['source_account_type'],
|
||||
'id' => $transaction['source_account_id'],
|
||||
'name' => $transaction['source_account_name'],
|
||||
'iban' => $transaction['source_account_iban'],
|
||||
];
|
||||
$accounts['destination'][] = [
|
||||
'type' => $transaction['destination_account_type'],
|
||||
'id' => $transaction['destination_account_id'],
|
||||
'name' => $transaction['destination_account_name'],
|
||||
'iban' => $transaction['destination_account_iban'],
|
||||
];
|
||||
}
|
||||
|
||||
$accounts['source'] = array_unique($accounts['source'], SORT_REGULAR);
|
||||
$accounts['destination'] = array_unique($accounts['destination'], SORT_REGULAR);
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
|
||||
private function getAmounts(array $group): array
|
||||
{
|
||||
$amounts = [];
|
||||
@@ -202,29 +227,4 @@ class ShowController extends Controller
|
||||
|
||||
return $amounts;
|
||||
}
|
||||
|
||||
private function getAccounts(array $group): array
|
||||
{
|
||||
$accounts = ['source' => [], 'destination' => []];
|
||||
|
||||
foreach ($group['transactions'] as $transaction) {
|
||||
$accounts['source'][] = [
|
||||
'type' => $transaction['source_account_type'],
|
||||
'id' => $transaction['source_account_id'],
|
||||
'name' => $transaction['source_account_name'],
|
||||
'iban' => $transaction['source_account_iban'],
|
||||
];
|
||||
$accounts['destination'][] = [
|
||||
'type' => $transaction['destination_account_type'],
|
||||
'id' => $transaction['destination_account_id'],
|
||||
'name' => $transaction['destination_account_name'],
|
||||
'iban' => $transaction['destination_account_iban'],
|
||||
];
|
||||
}
|
||||
|
||||
$accounts['source'] = array_unique($accounts['source'], SORT_REGULAR);
|
||||
$accounts['destination'] = array_unique($accounts['destination'], SORT_REGULAR);
|
||||
|
||||
return $accounts;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,22 @@ class Installer
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is access denied error.
|
||||
*/
|
||||
protected function isAccessDenied(string $message): bool
|
||||
{
|
||||
return false !== stripos($message, 'Access denied');
|
||||
}
|
||||
|
||||
/**
|
||||
* Is no tables exist error.
|
||||
*/
|
||||
protected function noTablesExist(string $message): bool
|
||||
{
|
||||
return false !== stripos($message, 'Base table or view not found');
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the tables are created and accounted for.
|
||||
*
|
||||
@@ -113,20 +129,4 @@ class Installer
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is access denied error.
|
||||
*/
|
||||
protected function isAccessDenied(string $message): bool
|
||||
{
|
||||
return false !== stripos($message, 'Access denied');
|
||||
}
|
||||
|
||||
/**
|
||||
* Is no tables exist error.
|
||||
*/
|
||||
protected function noTablesExist(string $message): bool
|
||||
{
|
||||
return false !== stripos($message, 'Base table or view not found');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,10 +82,31 @@ class InterestingMessage
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
private function testing(): bool
|
||||
private function accountMessage(Request $request): bool
|
||||
{
|
||||
// ignore middleware in test environment.
|
||||
return 'testing' === config('app.env') || !auth()->check();
|
||||
// get parameters from request.
|
||||
$accountId = $request->get('account_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $accountId && null !== $message;
|
||||
}
|
||||
|
||||
private function billMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$billId = $request->get('bill_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $billId && null !== $message;
|
||||
}
|
||||
|
||||
private function currencyMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$code = $request->get('code');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $code && null !== $message;
|
||||
}
|
||||
|
||||
private function groupMessage(Request $request): bool
|
||||
@@ -97,6 +118,85 @@ class InterestingMessage
|
||||
return null !== $transactionGroupId && null !== $message;
|
||||
}
|
||||
|
||||
private function handleAccountMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
$accountId = $request->get('account_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var null|Account $account */
|
||||
$account = $user->accounts()->withTrashed()->find($accountId);
|
||||
|
||||
if (null === $account) {
|
||||
return;
|
||||
}
|
||||
if ('deleted' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.account_deleted', ['name' => $account->name]));
|
||||
}
|
||||
if ('created' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name]));
|
||||
}
|
||||
if ('updated' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name]));
|
||||
}
|
||||
}
|
||||
|
||||
private function handleBillMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
$billId = $request->get('bill_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var null|Bill $bill */
|
||||
$bill = auth()->user()->bills()->withTrashed()->find($billId);
|
||||
|
||||
if (null === $bill) {
|
||||
return;
|
||||
}
|
||||
if ('deleted' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $bill->name]));
|
||||
}
|
||||
if ('created' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
|
||||
}
|
||||
}
|
||||
|
||||
private function handleCurrencyMessage(Request $request): void
|
||||
{
|
||||
// params:
|
||||
// get parameters from request.
|
||||
$code = (string) $request->get('code');
|
||||
$message = (string) $request->get('message');
|
||||
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyByCode($code);
|
||||
} catch (FireflyException) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ('enabled' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
|
||||
}
|
||||
if ('enable_failed' === $message) {
|
||||
session()->flash('error', (string) trans('firefly.could_not_enable_currency', ['name' => $currency->name]));
|
||||
}
|
||||
if ('disabled' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
|
||||
}
|
||||
if ('disable_failed' === $message) {
|
||||
session()->flash('error', (string) trans('firefly.could_not_disable_currency', ['name' => $currency->name]));
|
||||
}
|
||||
if ('default' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.new_default_currency', ['name' => $currency->name]));
|
||||
}
|
||||
if ('default_failed' === $message) {
|
||||
session()->flash('error', (string) trans('firefly.default_currency_failed', ['name' => $currency->name]));
|
||||
}
|
||||
}
|
||||
|
||||
private function handleGroupMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
@@ -135,15 +235,6 @@ class InterestingMessage
|
||||
}
|
||||
}
|
||||
|
||||
private function userGroupMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$transactionGroupId = $request->get('user_group_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $transactionGroupId && null !== $message;
|
||||
}
|
||||
|
||||
private function handleUserGroupMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
@@ -181,79 +272,6 @@ class InterestingMessage
|
||||
}
|
||||
}
|
||||
|
||||
private function accountMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$accountId = $request->get('account_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $accountId && null !== $message;
|
||||
}
|
||||
|
||||
private function handleAccountMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
$accountId = $request->get('account_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
|
||||
/** @var null|Account $account */
|
||||
$account = $user->accounts()->withTrashed()->find($accountId);
|
||||
|
||||
if (null === $account) {
|
||||
return;
|
||||
}
|
||||
if ('deleted' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.account_deleted', ['name' => $account->name]));
|
||||
}
|
||||
if ('created' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.stored_new_account', ['name' => $account->name]));
|
||||
}
|
||||
if ('updated' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.updated_account', ['name' => $account->name]));
|
||||
}
|
||||
}
|
||||
|
||||
private function billMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$billId = $request->get('bill_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $billId && null !== $message;
|
||||
}
|
||||
|
||||
private function handleBillMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
$billId = $request->get('bill_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
/** @var null|Bill $bill */
|
||||
$bill = auth()->user()->bills()->withTrashed()->find($billId);
|
||||
|
||||
if (null === $bill) {
|
||||
return;
|
||||
}
|
||||
if ('deleted' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.deleted_bill', ['name' => $bill->name]));
|
||||
}
|
||||
if ('created' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.stored_new_bill', ['name' => $bill->name]));
|
||||
}
|
||||
}
|
||||
|
||||
private function webhookMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$webhookId = $request->get('webhook_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $webhookId && null !== $message;
|
||||
}
|
||||
|
||||
private function handleWebhookMessage(Request $request): void
|
||||
{
|
||||
// get parameters from request.
|
||||
@@ -277,45 +295,27 @@ class InterestingMessage
|
||||
}
|
||||
}
|
||||
|
||||
private function currencyMessage(Request $request): bool
|
||||
private function testing(): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$code = $request->get('code');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $code && null !== $message;
|
||||
// ignore middleware in test environment.
|
||||
return 'testing' === config('app.env') || !auth()->check();
|
||||
}
|
||||
|
||||
private function handleCurrencyMessage(Request $request): void
|
||||
private function userGroupMessage(Request $request): bool
|
||||
{
|
||||
// params:
|
||||
// get parameters from request.
|
||||
$code = (string) $request->get('code');
|
||||
$message = (string) $request->get('message');
|
||||
$transactionGroupId = $request->get('user_group_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
try {
|
||||
$currency = Amount::getTransactionCurrencyByCode($code);
|
||||
} catch (FireflyException) {
|
||||
return;
|
||||
}
|
||||
return null !== $transactionGroupId && null !== $message;
|
||||
}
|
||||
|
||||
if ('enabled' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.currency_is_now_enabled', ['name' => $currency->name]));
|
||||
}
|
||||
if ('enable_failed' === $message) {
|
||||
session()->flash('error', (string) trans('firefly.could_not_enable_currency', ['name' => $currency->name]));
|
||||
}
|
||||
if ('disabled' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.currency_is_now_disabled', ['name' => $currency->name]));
|
||||
}
|
||||
if ('disable_failed' === $message) {
|
||||
session()->flash('error', (string) trans('firefly.could_not_disable_currency', ['name' => $currency->name]));
|
||||
}
|
||||
if ('default' === $message) {
|
||||
session()->flash('success', (string) trans('firefly.new_default_currency', ['name' => $currency->name]));
|
||||
}
|
||||
if ('default_failed' === $message) {
|
||||
session()->flash('error', (string) trans('firefly.default_currency_failed', ['name' => $currency->name]));
|
||||
}
|
||||
private function webhookMessage(Request $request): bool
|
||||
{
|
||||
// get parameters from request.
|
||||
$webhookId = $request->get('webhook_id');
|
||||
$message = $request->get('message');
|
||||
|
||||
return null !== $webhookId && null !== $message;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,37 +65,17 @@ class Range
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the range for the current view.
|
||||
* Configure the list length.
|
||||
*/
|
||||
private function setRange(): void
|
||||
private function configureList(): void
|
||||
{
|
||||
// ignore preference. set the range to be the current month:
|
||||
if (!app('session')->has('start') && !app('session')->has('end')) {
|
||||
Log::debug('setRange: Session has no start or end.');
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
if (is_array($viewRange)) {
|
||||
$viewRange = '1M';
|
||||
}
|
||||
$pref = Preferences::get('list-length', config('firefly.list_length', 10))->data;
|
||||
app('view')->share('listLength', $pref);
|
||||
|
||||
$today = today(config('app.timezone'));
|
||||
$start = Navigation::updateStartDate((string) $viewRange, $today);
|
||||
$end = Navigation::updateEndDate((string) $viewRange, $start);
|
||||
|
||||
app('session')->put('start', $start);
|
||||
app('session')->put('end', $end);
|
||||
}
|
||||
if (!app('session')->has('first')) {
|
||||
Log::debug('setRange: Session has no "first".');
|
||||
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$journal = $repository->firstNull();
|
||||
$first = today(config('app.timezone'))->startOfYear();
|
||||
|
||||
if (null !== $journal) {
|
||||
$first = $journal->date ?? $first;
|
||||
}
|
||||
app('session')->put('first', $first);
|
||||
// share security message:
|
||||
if (FireflyConfig::has('upgrade_security_message') && FireflyConfig::has('upgrade_security_level')) {
|
||||
app('view')->share('upgrade_security_message', FireflyConfig::get('upgrade_security_message')->data);
|
||||
app('view')->share('upgrade_security_level', FireflyConfig::get('upgrade_security_level')->data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,17 +116,37 @@ class Range
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the list length.
|
||||
* Set the range for the current view.
|
||||
*/
|
||||
private function configureList(): void
|
||||
private function setRange(): void
|
||||
{
|
||||
$pref = Preferences::get('list-length', config('firefly.list_length', 10))->data;
|
||||
app('view')->share('listLength', $pref);
|
||||
// ignore preference. set the range to be the current month:
|
||||
if (!app('session')->has('start') && !app('session')->has('end')) {
|
||||
Log::debug('setRange: Session has no start or end.');
|
||||
$viewRange = Preferences::get('viewRange', '1M')->data;
|
||||
if (is_array($viewRange)) {
|
||||
$viewRange = '1M';
|
||||
}
|
||||
|
||||
// share security message:
|
||||
if (FireflyConfig::has('upgrade_security_message') && FireflyConfig::has('upgrade_security_level')) {
|
||||
app('view')->share('upgrade_security_message', FireflyConfig::get('upgrade_security_message')->data);
|
||||
app('view')->share('upgrade_security_level', FireflyConfig::get('upgrade_security_level')->data);
|
||||
$today = today(config('app.timezone'));
|
||||
$start = Navigation::updateStartDate((string) $viewRange, $today);
|
||||
$end = Navigation::updateEndDate((string) $viewRange, $start);
|
||||
|
||||
app('session')->put('start', $start);
|
||||
app('session')->put('end', $end);
|
||||
}
|
||||
if (!app('session')->has('first')) {
|
||||
Log::debug('setRange: Session has no "first".');
|
||||
|
||||
/** @var JournalRepositoryInterface $repository */
|
||||
$repository = app(JournalRepositoryInterface::class);
|
||||
$journal = $repository->firstNull();
|
||||
$first = today(config('app.timezone'))->startOfYear();
|
||||
|
||||
if (null !== $journal) {
|
||||
$first = $journal->date ?? $first;
|
||||
}
|
||||
app('session')->put('first', $first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,35 +147,6 @@ class RecurrenceFormRequest extends FormRequest
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses repetition data.
|
||||
*/
|
||||
private function parseRepetitionData(): array
|
||||
{
|
||||
$value = $this->convertString('repetition_type');
|
||||
$return = ['type' => '', 'moment' => ''];
|
||||
|
||||
if ('daily' === $value) {
|
||||
$return['type'] = $value;
|
||||
}
|
||||
// monthly,17
|
||||
// ndom,3,7
|
||||
if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) {
|
||||
$return['type'] = substr($value, 0, 6);
|
||||
$return['moment'] = substr($value, 7);
|
||||
}
|
||||
if (str_starts_with($value, 'monthly')) {
|
||||
$return['type'] = substr($value, 0, 7);
|
||||
$return['moment'] = substr($value, 8);
|
||||
}
|
||||
if (str_starts_with($value, 'ndom')) {
|
||||
$return['type'] = substr($value, 0, 4);
|
||||
$return['moment'] = substr($value, 5);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* The rules for this request.
|
||||
*/
|
||||
@@ -262,20 +233,6 @@ class RecurrenceFormRequest extends FormRequest
|
||||
return $rules;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator);
|
||||
});
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the given account information. Switches on given transaction type.
|
||||
*
|
||||
@@ -340,4 +297,47 @@ class RecurrenceFormRequest extends FormRequest
|
||||
$validator->errors()->add('withdrawal_destination_id', $message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the validator instance with special rules for after the basic validation rules.
|
||||
*/
|
||||
public function withValidator(Validator $validator): void
|
||||
{
|
||||
$validator->after(function (Validator $validator): void {
|
||||
// validate all account info
|
||||
$this->validateAccountInformation($validator);
|
||||
});
|
||||
if ($validator->fails()) {
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses repetition data.
|
||||
*/
|
||||
private function parseRepetitionData(): array
|
||||
{
|
||||
$value = $this->convertString('repetition_type');
|
||||
$return = ['type' => '', 'moment' => ''];
|
||||
|
||||
if ('daily' === $value) {
|
||||
$return['type'] = $value;
|
||||
}
|
||||
// monthly,17
|
||||
// ndom,3,7
|
||||
if (in_array(substr($value, 0, 6), ['yearly', 'weekly'], true)) {
|
||||
$return['type'] = substr($value, 0, 6);
|
||||
$return['moment'] = substr($value, 7);
|
||||
}
|
||||
if (str_starts_with($value, 'monthly')) {
|
||||
$return['type'] = substr($value, 0, 7);
|
||||
$return['moment'] = substr($value, 8);
|
||||
}
|
||||
if (str_starts_with($value, 'ndom')) {
|
||||
$return['type'] = substr($value, 0, 4);
|
||||
$return['moment'] = substr($value, 5);
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,47 +41,6 @@ class RuleFormRequest extends FormRequest
|
||||
use ConvertsDataTypes;
|
||||
use GetRuleConfiguration;
|
||||
|
||||
/**
|
||||
* Get all data for controller.
|
||||
*/
|
||||
public function getRuleData(): array
|
||||
{
|
||||
return [
|
||||
'title' => $this->convertString('title'),
|
||||
'rule_group_id' => $this->convertInteger('rule_group_id'),
|
||||
'active' => $this->boolean('active'),
|
||||
'trigger' => $this->convertString('trigger'),
|
||||
'description' => $this->stringWithNewlines('description'),
|
||||
'stop_processing' => $this->boolean('stop_processing'),
|
||||
'strict' => $this->boolean('strict'),
|
||||
'run_after_form' => $this->boolean('run_after_form'),
|
||||
'triggers' => $this->getRuleTriggerData(),
|
||||
'actions' => $this->getRuleActionData(),
|
||||
];
|
||||
}
|
||||
|
||||
private function getRuleTriggerData(): array
|
||||
{
|
||||
$return = [];
|
||||
$triggerData = $this->get('triggers');
|
||||
if (is_array($triggerData)) {
|
||||
foreach ($triggerData as $trigger) {
|
||||
$stopProcessing = $trigger['stop_processing'] ?? '0';
|
||||
$prohibited = $trigger['prohibited'] ?? '0';
|
||||
$set = [
|
||||
'type' => $trigger['type'] ?? 'invalid',
|
||||
'value' => $trigger['value'] ?? '',
|
||||
'stop_processing' => 1 === (int) $stopProcessing,
|
||||
'prohibited' => 1 === (int) $prohibited,
|
||||
];
|
||||
$set = self::replaceAmountTrigger($set);
|
||||
$return[] = $set;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
public static function replaceAmountTrigger(array $array): array
|
||||
{
|
||||
// do some sneaky search and replace.
|
||||
@@ -107,22 +66,23 @@ class RuleFormRequest extends FormRequest
|
||||
return $array;
|
||||
}
|
||||
|
||||
private function getRuleActionData(): array
|
||||
/**
|
||||
* Get all data for controller.
|
||||
*/
|
||||
public function getRuleData(): array
|
||||
{
|
||||
$return = [];
|
||||
$actionData = $this->get('actions');
|
||||
if (is_array($actionData)) {
|
||||
foreach ($actionData as $action) {
|
||||
$stopProcessing = $action['stop_processing'] ?? '0';
|
||||
$return[] = [
|
||||
'type' => $action['type'] ?? 'invalid',
|
||||
'value' => $action['value'] ?? '',
|
||||
'stop_processing' => 1 === (int) $stopProcessing,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
return [
|
||||
'title' => $this->convertString('title'),
|
||||
'rule_group_id' => $this->convertInteger('rule_group_id'),
|
||||
'active' => $this->boolean('active'),
|
||||
'trigger' => $this->convertString('trigger'),
|
||||
'description' => $this->stringWithNewlines('description'),
|
||||
'stop_processing' => $this->boolean('stop_processing'),
|
||||
'strict' => $this->boolean('strict'),
|
||||
'run_after_form' => $this->boolean('run_after_form'),
|
||||
'triggers' => $this->getRuleTriggerData(),
|
||||
'actions' => $this->getRuleActionData(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,4 +130,44 @@ class RuleFormRequest extends FormRequest
|
||||
Log::channel('audit')->error(sprintf('Validation errors in %s', self::class), $validator->errors()->toArray());
|
||||
}
|
||||
}
|
||||
|
||||
private function getRuleActionData(): array
|
||||
{
|
||||
$return = [];
|
||||
$actionData = $this->get('actions');
|
||||
if (is_array($actionData)) {
|
||||
foreach ($actionData as $action) {
|
||||
$stopProcessing = $action['stop_processing'] ?? '0';
|
||||
$return[] = [
|
||||
'type' => $action['type'] ?? 'invalid',
|
||||
'value' => $action['value'] ?? '',
|
||||
'stop_processing' => 1 === (int) $stopProcessing,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
|
||||
private function getRuleTriggerData(): array
|
||||
{
|
||||
$return = [];
|
||||
$triggerData = $this->get('triggers');
|
||||
if (is_array($triggerData)) {
|
||||
foreach ($triggerData as $trigger) {
|
||||
$stopProcessing = $trigger['stop_processing'] ?? '0';
|
||||
$prohibited = $trigger['prohibited'] ?? '0';
|
||||
$set = [
|
||||
'type' => $trigger['type'] ?? 'invalid',
|
||||
'value' => $trigger['value'] ?? '',
|
||||
'stop_processing' => 1 === (int) $stopProcessing,
|
||||
'prohibited' => 1 === (int) $prohibited,
|
||||
];
|
||||
$set = self::replaceAmountTrigger($set);
|
||||
$return[] = $set;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user