mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-02-25 18:45:27 -06:00
Merge branch 'release/5.6.3'
This commit is contained in:
commit
7afe9fac0a
17
.env.example
17
.env.example
@ -177,6 +177,12 @@ MAP_DEFAULT_ZOOM=6
|
||||
# https://docs.firefly-iii.org/advanced-installation/authentication
|
||||
AUTHENTICATION_GUARD=web
|
||||
|
||||
#
|
||||
# Your LDAP server may speak a dialect. You can choose between 'OpenLDAP' and 'ActiveDirectory'
|
||||
# Anything else defaults to 'ActiveDirectory'
|
||||
#
|
||||
LDAP_DIALECT=OpenLDAP
|
||||
|
||||
#
|
||||
# LDAP connection settings:
|
||||
#
|
||||
@ -193,14 +199,13 @@ LDAP_PASSWORD=super_secret
|
||||
LDAP_AUTH_FIELD=uid
|
||||
|
||||
#
|
||||
# If you wish to only authenticate users from a specific group, use the
|
||||
# group filter. Leave empty or remove if not in use.
|
||||
# If you wish to only authenticate users from a specific group, use the base DN above.
|
||||
#
|
||||
# Example: cn=Administrators,dc=local,dc=com
|
||||
# If you require extra/special filters please use the LDAP_EXTRA_FILTER with a valid DN.
|
||||
#
|
||||
# The group filter will only be applied after the user is authenticated.
|
||||
# The extra filter will only be applied after the user is authenticated.
|
||||
#
|
||||
LDAP_GROUP_FILTER=
|
||||
LDAP_EXTRA_FILTER=
|
||||
|
||||
#
|
||||
# Remote user guard settings
|
||||
@ -297,6 +302,8 @@ FIREFLY_III_LAYOUT=v1
|
||||
# It won't work. It doesn't do ANYTHING. Don't believe the lies you read online. I'm not joking.
|
||||
# This configuration value WILL NOT HELP.
|
||||
#
|
||||
# Notable exception to this rule is Synology, which, according to some users, will use APP_URL to rewrite stuff.
|
||||
#
|
||||
# This variable is ONLY used in some of the emails Firefly III sends around. Nowhere else.
|
||||
# So when configuring anything WEB related this variable doesn't do anything. Nothing
|
||||
#
|
||||
|
7
.github/.mergify.yml
vendored
7
.github/.mergify.yml
vendored
@ -1,7 +0,0 @@
|
||||
pull_request_rules:
|
||||
- name: PR on main is never approved.
|
||||
conditions:
|
||||
- base=main
|
||||
actions:
|
||||
close:
|
||||
message: Please reopen this PR on the `develop` branch. Thank you.
|
4
.github/stale.yml
vendored
4
.github/stale.yml
vendored
@ -1,11 +1,11 @@
|
||||
# Configuration for probot-stale - https://github.com/probot/stale
|
||||
|
||||
# Number of days of inactivity before an Issue or Pull Request becomes stale
|
||||
daysUntilStale: 7
|
||||
daysUntilStale: 14
|
||||
|
||||
# Number of days of inactivity before a stale Issue or Pull Request is closed.
|
||||
# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.
|
||||
daysUntilClose: 7
|
||||
daysUntilClose: 14
|
||||
|
||||
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||
# - "[Status] Maybe Later"
|
||||
|
@ -1,58 +0,0 @@
|
||||
---
|
||||
build:
|
||||
nodes:
|
||||
analysis:
|
||||
project_setup:
|
||||
override: true
|
||||
tests:
|
||||
override:
|
||||
- php-scrutinizer-run
|
||||
checks:
|
||||
javascript: true
|
||||
php:
|
||||
align_assignments: true
|
||||
avoid_fixme_comments: true
|
||||
avoid_multiple_statements_on_same_line: true
|
||||
avoid_perl_style_comments: true
|
||||
avoid_todo_comments: true
|
||||
duplication: false
|
||||
encourage_single_quotes: true
|
||||
newline_at_end_of_file: true
|
||||
no_goto: true
|
||||
no_long_variable_names:
|
||||
maximum: "20"
|
||||
no_short_method_names:
|
||||
minimum: "3"
|
||||
no_short_variable_names:
|
||||
minimum: "3"
|
||||
optional_parameters_at_the_end: true
|
||||
parameter_doc_comments: true
|
||||
remove_extra_empty_lines: true
|
||||
return_doc_comment_if_not_inferrable: true
|
||||
return_doc_comments: true
|
||||
uppercase_constants: true
|
||||
use_self_instead_of_fqcn: true
|
||||
coding_style:
|
||||
php:
|
||||
spaces:
|
||||
around_operators:
|
||||
concatenation: true
|
||||
other:
|
||||
after_type_cast: false
|
||||
filter:
|
||||
excluded_paths:
|
||||
- database/migrations/*
|
||||
- bootstrap/*
|
||||
- config/*
|
||||
- docker/*
|
||||
- public/js/lib/*
|
||||
- public/lib/adminlte/js/*
|
||||
- public/lib/bootstrap/js/*
|
||||
- resources/*
|
||||
- routes/*
|
||||
- storage/*
|
||||
paths:
|
||||
- app/*
|
||||
- public/js/ff/*
|
||||
tools:
|
||||
external_code_coverage: false
|
20
.travis.yml
20
.travis.yml
@ -1,20 +0,0 @@
|
||||
language: php
|
||||
php:
|
||||
- '7.4'
|
||||
dist: xenial
|
||||
os: linux
|
||||
cache:
|
||||
directories:
|
||||
- "/home/travis/.config"
|
||||
- "/home/travis/build/firefly-iii/firefly-iii/vendor"
|
||||
|
||||
branches:
|
||||
only:
|
||||
- develop
|
||||
|
||||
before_script:
|
||||
- phpenv config-rm xdebug.ini || return 0
|
||||
|
||||
script:
|
||||
- "./.ci/phpstan.sh"
|
||||
- "./.ci/phpunit.sh"
|
@ -211,7 +211,7 @@ class StoreRequest extends FormRequest
|
||||
// budget, category, bill and piggy
|
||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser],
|
||||
'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser,'nullable'],
|
||||
'transactions.*.category_name' => 'between:1,255|nullable',
|
||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||
|
@ -154,6 +154,7 @@ class ExportData extends Command
|
||||
/**
|
||||
* @return array
|
||||
* @throws FireflyException
|
||||
* @throws Exception
|
||||
*/
|
||||
private function parseOptions(): array
|
||||
{
|
||||
@ -201,12 +202,17 @@ class ExportData extends Command
|
||||
$error = true;
|
||||
}
|
||||
}
|
||||
if(null === $this->option($field)) {
|
||||
Log::info(sprintf('No date given in field "%s"', $field));
|
||||
$error = true;
|
||||
}
|
||||
|
||||
if (true === $error && 'start' === $field) {
|
||||
$journal = $this->journalRepository->firstNull();
|
||||
$date = null === $journal ? Carbon::now()->subYear() : $journal->date;
|
||||
$date->startOfDay();
|
||||
}
|
||||
|
||||
if (true === $error && 'end' === $field) {
|
||||
$date = today(config('app.timezone'));
|
||||
$date->endOfDay();
|
||||
|
44
app/Events/ActuallyLoggedIn.php
Normal file
44
app/Events/ActuallyLoggedIn.php
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* ActuallyLoggedIn.php
|
||||
* Copyright (c) 2021 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class ActuallyLoggedIn
|
||||
*/
|
||||
class ActuallyLoggedIn extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public User $user;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
public function __construct(User $user)
|
||||
{
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
@ -25,6 +25,7 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
@ -116,9 +117,24 @@ class UserEventHandler
|
||||
*/
|
||||
public function createGroupMembership(RegisteredUser $event): bool
|
||||
{
|
||||
$user = $event->user;
|
||||
$user = $event->user;
|
||||
$groupExists = true;
|
||||
$groupTitle = $user->email;
|
||||
$index = 1;
|
||||
|
||||
// create a new group.
|
||||
$group = UserGroup::create(['title' => $user->email]);
|
||||
while (true === $groupExists) {
|
||||
$groupExists = UserGroup::where('title', $groupTitle)->count() > 0;
|
||||
if(false === $groupExists) {
|
||||
$group = UserGroup::create(['title' => $groupTitle]);
|
||||
break;
|
||||
}
|
||||
$groupTitle = sprintf('%s-%d', $user->email, $index);
|
||||
$index++;
|
||||
if($index > 99) {
|
||||
throw new FireflyException('Email address can no longer be used for registrations.');
|
||||
}
|
||||
}
|
||||
$role = UserRole::where('title', UserRole::OWNER)->first();
|
||||
if (null === $role) {
|
||||
throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?');
|
||||
@ -317,12 +333,11 @@ class UserEventHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Login $event
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @param ActuallyLoggedIn $event
|
||||
*/
|
||||
public function storeUserIPAddress(Login $event): void
|
||||
public function storeUserIPAddress(ActuallyLoggedIn $event): void
|
||||
{
|
||||
Log::debug('Now in storeUserIPAddress');
|
||||
/** @var User $user */
|
||||
$user = $event->user;
|
||||
/** @var array $preference */
|
||||
|
@ -79,7 +79,7 @@ class NetWorth implements NetWorthInterface
|
||||
|
||||
$netWorth = [];
|
||||
$result = [];
|
||||
Log::debug(sprintf('Now in getNetWorthByCurrency(%s)', $date->format('Y-m-d')));
|
||||
//Log::debug(sprintf('Now in getNetWorthByCurrency(%s)', $date->format('Y-m-d')));
|
||||
|
||||
// get default currency
|
||||
$default = app('amount')->getDefaultCurrencyByUser($this->user);
|
||||
@ -90,16 +90,16 @@ class NetWorth implements NetWorthInterface
|
||||
// get the preferred currency for this account
|
||||
/** @var Account $account */
|
||||
foreach ($accounts as $account) {
|
||||
Log::debug(sprintf('Now at account #%d: "%s"', $account->id, $account->name));
|
||||
//Log::debug(sprintf('Now at account #%d: "%s"', $account->id, $account->name));
|
||||
$currencyId = (int)$this->accountRepository->getMetaValue($account, 'currency_id');
|
||||
$currencyId = 0 === $currencyId ? $default->id : $currencyId;
|
||||
|
||||
Log::debug(sprintf('Currency ID is #%d', $currencyId));
|
||||
//Log::debug(sprintf('Currency ID is #%d', $currencyId));
|
||||
|
||||
// balance in array:
|
||||
$balance = $balances[$account->id] ?? '0';
|
||||
|
||||
Log::debug(sprintf('Balance is %s', $balance));
|
||||
//Log::debug(sprintf('Balance is %s', $balance));
|
||||
|
||||
// always subtract virtual balance.
|
||||
$virtualBalance = (string)$account->virtual_balance;
|
||||
@ -107,14 +107,14 @@ class NetWorth implements NetWorthInterface
|
||||
$balance = bcsub($balance, $virtualBalance);
|
||||
}
|
||||
|
||||
Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance));
|
||||
//Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance));
|
||||
|
||||
if (!array_key_exists($currencyId, $netWorth)) {
|
||||
$netWorth[$currencyId] = '0';
|
||||
}
|
||||
$netWorth[$currencyId] = bcadd($balance, $netWorth[$currencyId]);
|
||||
|
||||
Log::debug(sprintf('Total net worth for currency #%d is %s', $currencyId, $netWorth[$currencyId]));
|
||||
//Log::debug(sprintf('Total net worth for currency #%d is %s', $currencyId, $netWorth[$currencyId]));
|
||||
}
|
||||
ksort($netWorth);
|
||||
|
||||
|
@ -209,10 +209,10 @@ class PopupReport implements PopupReportInterface
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
|
||||
// set report accounts + the request accounts:
|
||||
$set = $attributes['accounts'] ?? new Collection;
|
||||
$set->push($account);
|
||||
//$set = $attributes['accounts'] ?? new Collection;
|
||||
//$set->push($account);
|
||||
|
||||
$collector->setBothAccounts($set)
|
||||
$collector->setDestinationAccounts(new Collection([$account]))
|
||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||
->withAccountInformation()
|
||||
->withBudgetInformation()
|
||||
@ -222,7 +222,6 @@ class PopupReport implements PopupReportInterface
|
||||
if (null !== $currency) {
|
||||
$collector->setCurrency($currency);
|
||||
}
|
||||
|
||||
return $collector->getExtractedJournals();
|
||||
}
|
||||
|
||||
|
@ -101,7 +101,7 @@ class IndexController extends Controller
|
||||
|
||||
$accounts->each(
|
||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
||||
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
||||
$account->lastActivityDate = $this->isInArrayDate($activities, $account->id);
|
||||
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||
@ -163,7 +163,7 @@ class IndexController extends Controller
|
||||
$accounts->each(
|
||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
||||
// See reference nr. 68
|
||||
$account->lastActivityDate = $this->isInArray($activities, $account->id);
|
||||
$account->lastActivityDate = $this->isInArrayDate($activities, $account->id);
|
||||
$account->startBalance = $this->isInArray($startBalances, $account->id);
|
||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Auth;
|
||||
use Adldap;
|
||||
use Cookie;
|
||||
use DB;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Providers\RouteServiceProvider;
|
||||
@ -119,6 +120,10 @@ class LoginController extends Controller
|
||||
|
||||
// if you just logged in, it can't be that you have a valid 2FA cookie.
|
||||
|
||||
// send a custom login event because laravel will also fire a login event if a "remember me"-cookie
|
||||
// restores the event.
|
||||
event(new ActuallyLoggedIn($this->guard()->user()));
|
||||
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
Log::warning('Login attempt failed.');
|
||||
|
@ -59,7 +59,6 @@ class ReportController extends Controller
|
||||
'category-entry' => $this->categoryEntry($attributes),
|
||||
'budget-entry' => $this->budgetEntry($attributes),
|
||||
};
|
||||
|
||||
return response()->json(['html' => $html]);
|
||||
}
|
||||
}
|
||||
|
@ -648,10 +648,9 @@ class CategoryController extends Controller
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
*
|
||||
* @return mixed|string
|
||||
* @throws JsonException
|
||||
* @return string
|
||||
*/
|
||||
public function operations(Collection $accounts, Carbon $start, Carbon $end)
|
||||
public function operations(Collection $accounts, Carbon $start, Carbon $end): string
|
||||
{
|
||||
// chart properties for cache:
|
||||
$cache = new CacheProperties;
|
||||
@ -673,7 +672,7 @@ class CategoryController extends Controller
|
||||
|
||||
|
||||
try {
|
||||
$result = prefixView('reports.partials.categories', compact('report'))->render();
|
||||
$result = (string)prefixView('reports.partials.categories', compact('report'))->render();
|
||||
$cache->store($result);
|
||||
} catch (Throwable $e) { // @phpstan-ignore-line
|
||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
||||
|
@ -34,6 +34,7 @@ use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
@ -237,15 +238,17 @@ class CreateController extends Controller
|
||||
/**
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return RedirectResponse
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function duplicate(Rule $rule): RedirectResponse
|
||||
public function duplicate(Request $request): JsonResponse
|
||||
{
|
||||
$newRule = $this->ruleRepos->duplicate($rule);
|
||||
$ruleId = (int)$request->get('id');
|
||||
$rule = $this->ruleRepos->find($ruleId);
|
||||
if (null !== $rule) {
|
||||
$this->ruleRepos->duplicate($rule);
|
||||
}
|
||||
|
||||
session()->flash('success', trans('firefly.duplicated_rule', ['title' => $rule->title, 'newTitle' => $newRule->title]));
|
||||
|
||||
return redirect(route('rules.index'));
|
||||
return new JsonResponse(['OK']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -28,6 +28,7 @@ use FireflyIII\Http\Requests\RuleGroupFormRequest;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
@ -62,24 +63,38 @@ class EditController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a rule group down.
|
||||
* Move a rule group in either direction.
|
||||
*
|
||||
* @param RuleGroup $ruleGroup
|
||||
* @param Request $request
|
||||
*
|
||||
* @return RedirectResponse|Redirector
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function down(RuleGroup $ruleGroup)
|
||||
public function moveGroup(Request $request): JsonResponse
|
||||
{
|
||||
$maxOrder = $this->repository->maxOrder();
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order < $maxOrder) {
|
||||
$newOrder = $order + 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
$groupId = (int)$request->get('id');
|
||||
$ruleGroup= $this->repository->find($groupId);
|
||||
if(null !== $ruleGroup) {
|
||||
$direction = $request->get('direction');
|
||||
if('down' === $direction) {
|
||||
$maxOrder = $this->repository->maxOrder();
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order < $maxOrder) {
|
||||
$newOrder = $order + 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
}
|
||||
}
|
||||
if('up' === $direction) {
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order > 1) {
|
||||
$newOrder = $order - 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return redirect(route('rules.index'));
|
||||
return new JsonResponse(['OK']);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Edit a rule group.
|
||||
*
|
||||
@ -106,25 +121,6 @@ class EditController extends Controller
|
||||
return prefixView('rules.rule-group.edit', compact('ruleGroup', 'subTitle'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the rule group up.
|
||||
*
|
||||
* @param RuleGroup $ruleGroup
|
||||
*
|
||||
* @return RedirectResponse|Redirector
|
||||
*
|
||||
*/
|
||||
public function up(RuleGroup $ruleGroup)
|
||||
{
|
||||
$order = (int)$ruleGroup->order;
|
||||
if ($order > 1) {
|
||||
$newOrder = $order - 1;
|
||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
||||
}
|
||||
|
||||
return redirect(route('rules.index'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rule group.
|
||||
*
|
||||
|
@ -32,9 +32,6 @@ use Illuminate\Contracts\Config\Repository;
|
||||
*/
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
/** @var int The headers to check. */
|
||||
//protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
||||
|
||||
/**
|
||||
* TrustProxies constructor.
|
||||
*
|
||||
@ -42,11 +39,7 @@ class TrustProxies extends Middleware
|
||||
*/
|
||||
public function __construct(Repository $config)
|
||||
{
|
||||
$trustedProxies = (string)config('firefly.trusted_proxies');
|
||||
$this->proxies = explode(',', $trustedProxies);
|
||||
if ('**' === $trustedProxies) {
|
||||
$this->proxies = '**';
|
||||
}
|
||||
$this->proxies = (string)config('firefly.trusted_proxies');
|
||||
parent::__construct($config);
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class BillStoreRequest extends FormRequest
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ class BillUpdateRequest extends FormRequest
|
||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||
'date' => 'required|date',
|
||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||
'skip' => 'required|between:0,31',
|
||||
'skip' => 'required|integer|gte:0|lte:31',
|
||||
'active' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ class BudgetFormStoreRequest extends FormRequest
|
||||
return [
|
||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||
'active' => 'numeric|between:0,1',
|
||||
'auto_budget_type' => 'numeric|between:0,2',
|
||||
'auto_budget_type' => 'numeric|integer|gte:0|lte:2',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
|
@ -73,7 +73,7 @@ class BudgetFormUpdateRequest extends FormRequest
|
||||
return [
|
||||
'name' => $nameRule,
|
||||
'active' => 'numeric|between:0,1',
|
||||
'auto_budget_type' => 'numeric|between:0,2',
|
||||
'auto_budget_type' => 'numeric|integer|gte:0|lte:31',
|
||||
'auto_budget_currency_id' => 'exists:transaction_currencies,id',
|
||||
'auto_budget_amount' => 'min:0|max:1000000000|required_if:auto_budget_type,1|required_if:auto_budget_type,2',
|
||||
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||
|
@ -192,7 +192,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
|
||||
'first_date' => 'required|date|after:' . $today->format('Y-m-d'),
|
||||
'repetition_type' => ['required', new ValidRecurrenceRepetitionValue, new ValidRecurrenceRepetitionType, 'between:1,20'],
|
||||
'skip' => 'required|numeric|between:0,31',
|
||||
'skip' => 'required|numeric|integer|gte:0|lte:31',
|
||||
|
||||
// optional for recurrence:
|
||||
'recurring_description' => 'between:0,65000',
|
||||
|
@ -431,7 +431,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
'user' => $recurrence->user_id,
|
||||
'currency_id' => (int)$transaction->transaction_currency_id,
|
||||
'currency_code' => null,
|
||||
'description' => $recurrence->recurrenceTransactions()->first()->description,
|
||||
'description' => $transactions->first()->description,
|
||||
'amount' => $transaction->amount,
|
||||
'budget_id' => $this->repository->getBudget($transaction),
|
||||
'budget_name' => null,
|
||||
@ -452,7 +452,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
||||
'tags' => $this->repository->getTags($transaction),
|
||||
'piggy_bank_id' => $this->repository->getPiggyBank($transaction),
|
||||
'piggy_bank_name' => null,
|
||||
'bill_id' => null,
|
||||
'bill_id' => $this->repository->getBillId($transaction),
|
||||
'bill_name' => null,
|
||||
'recurrence_total' => $total,
|
||||
'recurrence_count' => $count,
|
||||
|
@ -4,7 +4,8 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Ldap\Rules;
|
||||
|
||||
use LdapRecord\Laravel\Auth\Rule;
|
||||
use LdapRecord\Models\ActiveDirectory\Group;
|
||||
use LdapRecord\Models\Attributes\DistinguishedName;
|
||||
use LdapRecord\Query\ObjectNotFoundException;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@ -16,22 +17,51 @@ class UserDefinedRule extends Rule
|
||||
* Check if the rule passes validation.
|
||||
*
|
||||
* @return bool
|
||||
* @throws ObjectNotFoundException
|
||||
*/
|
||||
public function isValid()
|
||||
{
|
||||
// LDAP_GROUP_FILTER
|
||||
$groupFilter = config('ldap.group_filter');
|
||||
Log::debug(sprintf('UserDefinedRule with group filter "%s"', $groupFilter));
|
||||
if (null !== $groupFilter && '' !== (string)$groupFilter) {
|
||||
Log::debug('Group filter is not empty, will now apply it.');
|
||||
$administrators = Group::find($groupFilter);
|
||||
$result = $this->user->groups()->recursive()->exists($administrators);
|
||||
Log::debug(sprintf('Search result is %s.', var_export($result, true)));
|
||||
$extraFilter = config('ldap.extra_filter');
|
||||
Log::debug(sprintf('UserDefinedRule with extra filter "%s"', $extraFilter));
|
||||
|
||||
return $result;
|
||||
if (empty($extraFilter)) {
|
||||
Log::debug('Extra filter is empty, return true.');
|
||||
|
||||
return true;
|
||||
}
|
||||
Log::debug('Group filter is empty or NULL, so will return true.');
|
||||
Log::debug('Extra filter is not empty, continue.');
|
||||
|
||||
return true;
|
||||
// group class:
|
||||
// use ;
|
||||
$openLDAP = class_exists(\LdapRecord\Models\OpenLDAP\Group::class) ? \LdapRecord\Models\OpenLDAP\Group::class : '';
|
||||
$activeDirectory = class_exists(\LdapRecord\Models\ActiveDirectory\Group::class) ? \LdapRecord\Models\ActiveDirectory\Group::class : '';
|
||||
$groupClass = config('ldap.dialect') === 'OpenLDAP' ? $openLDAP : $activeDirectory;
|
||||
|
||||
Log::debug(sprintf('Will use dialect group class "%s"', $groupClass));
|
||||
|
||||
|
||||
// We've been given an invalid group filter. We will assume the
|
||||
// developer is using some group ANR attribute, and attempt
|
||||
// to check the user's membership with the resulting group.
|
||||
if (!DistinguishedName::isValid($extraFilter)) {
|
||||
Log::debug('UserDefinedRule: Is not valid DN');
|
||||
|
||||
return $this->user->groups()->recursive()->exists($groupClass::findByAnrOrFail($extraFilter));
|
||||
}
|
||||
|
||||
$head = strtolower(DistinguishedName::make($extraFilter)->head());
|
||||
Log::debug(sprintf('UserDefinedRule: Head is "%s"', $head));
|
||||
// If the head of the DN we've been given is an OU, we will assume
|
||||
// the developer is looking to filter users based on hierarchy.
|
||||
// Otherwise, we'll attempt locating a group by the given
|
||||
// group filter and checking the users group membership.
|
||||
if ('ou' === $head) {
|
||||
Log::debug('UserDefinedRule: Will return if user is a descendant of.');
|
||||
|
||||
return $this->user->isDescendantOf($extraFilter);
|
||||
}
|
||||
Log::debug('UserDefinedRule: Will return if user exists in group.');
|
||||
|
||||
return $this->user->groups()->recursive()->exists($groupClass::findOrFail($extraFilter));
|
||||
}
|
||||
}
|
||||
|
28
app/Ldap/Scopes/UserDefinedScope.php
Normal file
28
app/Ldap/Scopes/UserDefinedScope.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Ldap\Scopes;
|
||||
|
||||
use LdapRecord\Models\Model;
|
||||
use LdapRecord\Models\Scope;
|
||||
use LdapRecord\Query\Model\Builder;
|
||||
use Log;
|
||||
|
||||
|
||||
/**
|
||||
* Class UserDefinedScope
|
||||
*/
|
||||
class UserDefinedScope implements Scope
|
||||
{
|
||||
/**
|
||||
* Apply the scope to the given query.
|
||||
*
|
||||
* @param Builder $query
|
||||
* @param Model $model
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function apply(Builder $query, Model $model)
|
||||
{
|
||||
}
|
||||
}
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\ActuallyLoggedIn;
|
||||
use FireflyIII\Events\AdminRequestedTestMessage;
|
||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
@ -74,6 +75,8 @@ class EventServiceProvider extends ServiceProvider
|
||||
Login::class => [
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
||||
],
|
||||
ActuallyLoggedIn::class => [
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
||||
],
|
||||
DetectedNewIPAddress::class => [
|
||||
|
@ -251,7 +251,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
$journalIds = $set->pluck('id')->toArray();
|
||||
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||
$sum = bcadd($sum, $amount);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $amount, $sum));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $amount, $sum));
|
||||
}
|
||||
}
|
||||
|
||||
@ -281,7 +281,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||
$return[$currencyId] = bcadd($amount, $return[$currencyId]);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (currency %d)', $amount, $return[$currencyId], $currencyId));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (currency %d)', $amount, $return[$currencyId], $currencyId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,18 +302,18 @@ class BillRepository implements BillRepositoryInterface
|
||||
$sum = '0';
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
//Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||
$total = $dates->count() - $count;
|
||||
|
||||
Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
//Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
|
||||
if ($total > 0) {
|
||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||
$multi = bcmul($average, (string)$total);
|
||||
$sum = bcadd($sum, $multi);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $multi, $sum));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f', $multi, $sum));
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,20 +334,20 @@ class BillRepository implements BillRepositoryInterface
|
||||
$return = [];
|
||||
/** @var Bill $bill */
|
||||
foreach ($bills as $bill) {
|
||||
Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
//Log::debug(sprintf('Now at bill #%d (%s)', $bill->id, $bill->name));
|
||||
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||
$total = $dates->count() - $count;
|
||||
$currencyId = (int)$bill->transaction_currency_id;
|
||||
|
||||
Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
//Log::debug(sprintf('Dates = %d, journalCount = %d, total = %d', $dates->count(), $count, $total));
|
||||
|
||||
if ($total > 0) {
|
||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||
$multi = bcmul($average, (string)$total);
|
||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||
$return[$currencyId] = bcadd($return[$currencyId], $multi);
|
||||
Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (for currency %d)', $multi, $return[$currencyId], $currencyId));
|
||||
//Log::debug(sprintf('Total > 0, so add to sum %f, which becomes %f (for currency %d)', $multi, $return[$currencyId], $currencyId));
|
||||
}
|
||||
}
|
||||
|
||||
@ -481,7 +481,7 @@ class BillRepository implements BillRepositoryInterface
|
||||
//Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d')));
|
||||
|
||||
while ($currentStart <= $end) {
|
||||
Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
|
||||
//Log::debug(sprintf('Currentstart is now %s.', $currentStart->format('Y-m-d')));
|
||||
$nextExpectedMatch = $this->nextDateMatch($bill, $currentStart);
|
||||
//Log::debug(sprintf('Next Date match after %s is %s', $currentStart->format('Y-m-d'), $nextExpectedMatch->format('Y-m-d')));
|
||||
if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue
|
||||
|
@ -35,8 +35,7 @@ use Illuminate\Support\Collection;
|
||||
*/
|
||||
class OperationsRepository implements OperationsRepositoryInterface
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the withdrawal transaction journals (as arrays) set in that period
|
||||
@ -340,4 +339,136 @@ class OperationsRepository implements OperationsRepositoryInterface
|
||||
{
|
||||
return $this->user->categories()->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
|
||||
->setDestinationAccounts($accounts)->excludeSourceAccounts($accounts);
|
||||
if (null !== $categories && $categories->count() > 0) {
|
||||
$collector->setCategories($categories);
|
||||
}
|
||||
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
|
||||
$collector->setCategories($this->getCategories());
|
||||
}
|
||||
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$array = [];
|
||||
|
||||
foreach ($journals as $journal) {
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$categoryId = (int)$journal['category_id'];
|
||||
$categoryName = (string)$journal['category_name'];
|
||||
|
||||
// catch "no category" entries.
|
||||
if (0 === $categoryId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// info about the currency:
|
||||
$array[$currencyId] = $array[$currencyId] ?? [
|
||||
'categories' => [],
|
||||
'currency_id' => $currencyId,
|
||||
'currency_name' => $journal['currency_name'],
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||
];
|
||||
|
||||
// info about the categories:
|
||||
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
|
||||
'id' => $categoryId,
|
||||
'name' => $categoryName,
|
||||
'transaction_journals' => [],
|
||||
];
|
||||
|
||||
// add journal to array:
|
||||
// only a subset of the fields.
|
||||
$journalId = (int)$journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
|
||||
'amount' => app('steam')->positive($journal['amount']),
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => $journal['source_account_id'],
|
||||
'category_name' => $journal['category_name'],
|
||||
'source_account_name' => $journal['source_account_name'],
|
||||
'destination_account_id' => $journal['destination_account_id'],
|
||||
'destination_account_name' => $journal['destination_account_name'],
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array
|
||||
{
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->user)->setRange($start, $end)->setTypes([TransactionType::TRANSFER])
|
||||
->setSourceAccounts($accounts)->excludeDestinationAccounts($accounts);
|
||||
if (null !== $categories && $categories->count() > 0) {
|
||||
$collector->setCategories($categories);
|
||||
}
|
||||
if (null === $categories || (null !== $categories && 0 === $categories->count())) {
|
||||
$collector->setCategories($this->getCategories());
|
||||
}
|
||||
$collector->withCategoryInformation()->withAccountInformation()->withBudgetInformation();
|
||||
$journals = $collector->getExtractedJournals();
|
||||
$array = [];
|
||||
|
||||
foreach ($journals as $journal) {
|
||||
$currencyId = (int)$journal['currency_id'];
|
||||
$categoryId = (int)$journal['category_id'];
|
||||
$categoryName = (string)$journal['category_name'];
|
||||
|
||||
// catch "no category" entries.
|
||||
if (0 === $categoryId) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// info about the currency:
|
||||
$array[$currencyId] = $array[$currencyId] ?? [
|
||||
'categories' => [],
|
||||
'currency_id' => $currencyId,
|
||||
'currency_name' => $journal['currency_name'],
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
'currency_code' => $journal['currency_code'],
|
||||
'currency_decimal_places' => $journal['currency_decimal_places'],
|
||||
];
|
||||
|
||||
// info about the categories:
|
||||
$array[$currencyId]['categories'][$categoryId] = $array[$currencyId]['categories'][$categoryId] ?? [
|
||||
'id' => $categoryId,
|
||||
'name' => $categoryName,
|
||||
'transaction_journals' => [],
|
||||
];
|
||||
|
||||
// add journal to array:
|
||||
// only a subset of the fields.
|
||||
$journalId = (int)$journal['transaction_journal_id'];
|
||||
$array[$currencyId]['categories'][$categoryId]['transaction_journals'][$journalId] = [
|
||||
'amount' => app('steam')->negative($journal['amount']),
|
||||
'date' => $journal['date'],
|
||||
'source_account_id' => $journal['source_account_id'],
|
||||
'category_name' => $journal['category_name'],
|
||||
'source_account_name' => $journal['source_account_name'],
|
||||
'destination_account_id' => $journal['destination_account_id'],
|
||||
'destination_account_name' => $journal['destination_account_name'],
|
||||
'description' => $journal['description'],
|
||||
'transaction_group_id' => $journal['transaction_group_id'],
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,36 @@ interface OperationsRepositoryInterface
|
||||
*/
|
||||
public function listExpenses(Carbon $start, Carbon $end, ?Collection $accounts = null, ?Collection $categories = null): array;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the transfer transaction journals (as arrays) set in that period
|
||||
* which have the specified category set to them, transferred INTO the listed accounts.
|
||||
* It excludes any transfers between the listed accounts.
|
||||
* It's grouped per currency, with as few details in the array as possible. Amounts are always negative.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection|null $categories
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listTransferredIn(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the transfer transaction journals (as arrays) set in that period
|
||||
* which have the specified category set to them, transferred FROM the listed accounts.
|
||||
* It excludes any transfers between the listed accounts.
|
||||
* It's grouped per currency, with as few details in the array as possible. Amounts are always negative.
|
||||
*
|
||||
* @param Carbon $start
|
||||
* @param Carbon $end
|
||||
* @param Collection $accounts
|
||||
* @param Collection|null $categories
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function listTransferredOut(Carbon $start, Carbon $end, Collection $accounts, ?Collection $categories = null): array;
|
||||
|
||||
/**
|
||||
* This method returns a list of all the deposit transaction journals (as arrays) set in that period
|
||||
* which have the specified category set to them. It's grouped per currency, with as few details in the array
|
||||
|
@ -613,4 +613,20 @@ class RecurringRepository implements RecurringRepositoryInterface
|
||||
|
||||
return $filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function getBillId(RecurrenceTransaction $recTransaction): ?int
|
||||
{
|
||||
$return = null;
|
||||
/** @var RecurrenceTransactionMeta $meta */
|
||||
foreach ($recTransaction->recurrenceTransactionMeta as $meta) {
|
||||
if ('bill_id' === $meta->name) {
|
||||
$return = (int)$meta->value;
|
||||
}
|
||||
}
|
||||
|
||||
return $return;
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,15 @@ interface RecurringRepositoryInterface
|
||||
*/
|
||||
public function getCategoryId(RecurrenceTransaction $recTransaction): ?int;
|
||||
|
||||
/**
|
||||
* Get the category from a recurring transaction transaction.
|
||||
*
|
||||
* @param RecurrenceTransaction $recTransaction
|
||||
*
|
||||
* @return null|int
|
||||
*/
|
||||
public function getBillId(RecurrenceTransaction $recTransaction): ?int;
|
||||
|
||||
/**
|
||||
* Get the category from a recurring transaction transaction.
|
||||
*
|
||||
|
@ -329,10 +329,8 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
*/
|
||||
public function resetOrder(): bool
|
||||
{
|
||||
$this->user->ruleGroups()->where('active', false)->update(['order' => 0]);
|
||||
$set = $this->user
|
||||
->ruleGroups()
|
||||
->where('active', true)
|
||||
->whereNull('deleted_at')
|
||||
->orderBy('order', 'ASC')
|
||||
->orderBy('title', 'DESC')
|
||||
@ -363,7 +361,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
{
|
||||
$set = $ruleGroup->rules()
|
||||
->orderBy('order', 'ASC')
|
||||
->where('active', true)
|
||||
->orderBy('title', 'DESC')
|
||||
->orderBy('updated_at', 'DESC')
|
||||
->get(['rules.*']);
|
||||
|
@ -26,6 +26,7 @@ use Exception;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Models\BudgetLimit;
|
||||
use FireflyIII\Models\Role;
|
||||
use FireflyIII\Models\UserGroup;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
@ -164,7 +165,10 @@ class UserRepository implements UserRepositoryInterface
|
||||
public function destroy(User $user): bool
|
||||
{
|
||||
Log::debug(sprintf('Calling delete() on user %d', $user->id));
|
||||
|
||||
$user->groupMemberships()->delete();
|
||||
$user->delete();
|
||||
$this->deleteEmptyGroups();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -396,4 +400,20 @@ class UserRepository implements UserRepositoryInterface
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function deleteEmptyGroups(): void
|
||||
{
|
||||
$groups = UserGroup::get();
|
||||
/** @var UserGroup $group */
|
||||
foreach ($groups as $group) {
|
||||
$count = $group->groupMemberships()->count();
|
||||
if (0 === $count) {
|
||||
Log::info(sprintf('Deleted empty group #%d ("%s")', $group->id, $group->title));
|
||||
$group->delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,11 @@ interface UserRepositoryInterface
|
||||
*/
|
||||
public function all(): Collection;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public function deleteEmptyGroups(): void;
|
||||
|
||||
/**
|
||||
* Gives a user a role.
|
||||
*
|
||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Support\Http\Controllers;
|
||||
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Trait BasicDataSupport
|
||||
*
|
||||
@ -30,15 +32,28 @@ namespace FireflyIII\Support\Http\Controllers;
|
||||
trait BasicDataSupport
|
||||
{
|
||||
/**
|
||||
* Find the ID in a given array. Return '0' of not there (amount).
|
||||
* Find the ID in a given array. Return '0' if not there (amount).
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $entryId
|
||||
*
|
||||
* @return null|mixed
|
||||
*/
|
||||
protected function isInArray(array $array, int $entryId) // helper for data (math, calculations)
|
||||
protected function isInArray(array $array, int $entryId)
|
||||
{
|
||||
return $array[$entryId] ?? '0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the ID in a given array. Return null if not there (amount).
|
||||
*
|
||||
* @param array $array
|
||||
* @param int $entryId
|
||||
*
|
||||
* @return null|Carbon
|
||||
*/
|
||||
protected function isInArrayDate(array $array, int $entryId): ?Carbon
|
||||
{
|
||||
return $array[$entryId] ?? null;
|
||||
}
|
||||
}
|
||||
|
@ -249,7 +249,7 @@ class BudgetReportGenerator
|
||||
'budget_limits' => [],
|
||||
];
|
||||
|
||||
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end);
|
||||
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts);
|
||||
foreach ($noBudget as $noBudgetEntry) {
|
||||
|
||||
// currency information:
|
||||
|
@ -66,6 +66,11 @@ class CategoryReportGenerator
|
||||
{
|
||||
$earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts);
|
||||
$spentWith = $this->opsRepository->listExpenses($this->start, $this->end, $this->accounts);
|
||||
|
||||
// also transferred out and transferred into these accounts in this category:
|
||||
$transferredIn = $this->opsRepository->listTransferredIn($this->start, $this->end, $this->accounts);
|
||||
$transferredOut = $this->opsRepository->listTransferredOut($this->start, $this->end, $this->accounts);
|
||||
|
||||
$earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts);
|
||||
$spentWithout = $this->noCatRepository->listExpenses($this->start, $this->end, $this->accounts);
|
||||
|
||||
@ -75,7 +80,7 @@ class CategoryReportGenerator
|
||||
];
|
||||
|
||||
// needs four for-each loops.
|
||||
foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout] as $data) {
|
||||
foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout, $transferredIn, $transferredOut] as $data) {
|
||||
$this->processOpsArray($data);
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
$parser = new QueryParser();
|
||||
try {
|
||||
$query1 = $parser->parse($query);
|
||||
} catch (TypeError|LogicException $e) {
|
||||
} catch (TypeError | LogicException $e) {
|
||||
Log::error($e->getMessage());
|
||||
Log::error(sprintf('Could not parse search: "%s".', $query));
|
||||
throw new FireflyException('Invalid search value. See the logs.', 0, $e);
|
||||
@ -245,6 +245,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
private function handleSearchNode(Node $searchNode): void
|
||||
{
|
||||
$class = get_class($searchNode);
|
||||
Log::debug(sprintf('Now in handleSearchNode(%s)', $class));
|
||||
switch ($class) {
|
||||
default:
|
||||
Log::error(sprintf('Cannot handle node %s', $class));
|
||||
@ -252,7 +253,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
case Subquery::class:
|
||||
// loop all notes in subquery:
|
||||
foreach ($searchNode->getNodes() as $subNode) { // @phpstan-ignore-line
|
||||
$this->handleSearchNode($subNode); // lets hope its not too recursive!
|
||||
$this->handleSearchNode($subNode); // let's hope it's not too recursive!
|
||||
}
|
||||
break;
|
||||
case Word::class:
|
||||
@ -282,13 +283,15 @@ class OperatorQuerySearch implements SearchInterface
|
||||
'value' => (string)$value,
|
||||
]
|
||||
);
|
||||
} else {
|
||||
Log::debug(sprintf('Added operator type "%s"', $operator));
|
||||
}
|
||||
if (!in_array($operator, $this->validOperators, true)) {
|
||||
Log::debug(sprintf('Added INVALID operator type "%s"', $operator));
|
||||
$this->invalidOperators[] = [
|
||||
'type' => $operator,
|
||||
'value' => (string)$value,
|
||||
];
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
@ -302,7 +305,7 @@ class OperatorQuerySearch implements SearchInterface
|
||||
*/
|
||||
private function updateCollector(string $operator, string $value): bool
|
||||
{
|
||||
Log::debug(sprintf('updateCollector("%s", "%s")', $operator, $value));
|
||||
Log::debug(sprintf('Now in updateCollector("%s", "%s")', $operator, $value));
|
||||
|
||||
// check if alias, replace if necessary:
|
||||
$operator = self::getRootOperator($operator);
|
||||
|
@ -406,7 +406,7 @@ class Steam
|
||||
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]); // @phpstan-ignore-line
|
||||
|
||||
foreach ($set as $entry) {
|
||||
$date = new Carbon($entry->max_date, 'UTC');
|
||||
$date = new Carbon($entry->max_date, config('app.timezone'));
|
||||
$date->setTimezone(config('app.timezone'));
|
||||
$list[(int)$entry->account_id] = $date;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ class General extends AbstractExtension
|
||||
$this->mimeIcon(),
|
||||
$this->markdown(),
|
||||
$this->floatval(),
|
||||
$this->phpHostName(),
|
||||
];
|
||||
}
|
||||
|
||||
@ -91,6 +92,24 @@ class General extends AbstractExtension
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show URL host name
|
||||
*
|
||||
* @return TwigFilter
|
||||
*/
|
||||
protected function phpHostName(): TwigFilter
|
||||
{
|
||||
return new TwigFilter(
|
||||
'phphost',
|
||||
static function (string $string): string {
|
||||
$proto = (string)parse_url($string, PHP_URL_SCHEME);
|
||||
$host = (string)parse_url($string, PHP_URL_HOST);
|
||||
|
||||
return e(sprintf('%s://%s', $proto, $host));
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to convert 1024 to 1kb etc.
|
||||
*
|
||||
|
@ -60,9 +60,11 @@ class DeleteTransaction implements ActionInterface
|
||||
|
||||
// trigger delete factory:
|
||||
$journal = TransactionJournal::find($journal['transaction_group_id']);
|
||||
/** @var JournalDestroyService $service */
|
||||
$service = app(JournalDestroyService::class);
|
||||
$service->destroy($journal);
|
||||
if (null !== $journal) {
|
||||
/** @var JournalDestroyService $service */
|
||||
$service = app(JournalDestroyService::class);
|
||||
$service->destroy($journal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
||||
$bill = $this->getBill($journal->bill);
|
||||
|
||||
if (null !== $foreignAmount && null !== $foreignCurrency) {
|
||||
$foreignAmount = number_format((float)$foreignAmount, $foreignCurrency->decimal_places ?? 0, '.', '');
|
||||
$foreignAmount = number_format((float)$foreignAmount, $foreignCurrency['decimal_places'] ?? 0, '.', '');
|
||||
}
|
||||
|
||||
$longitude = null;
|
||||
|
@ -67,7 +67,7 @@ trait TransferValidation
|
||||
return false;
|
||||
}
|
||||
|
||||
// otherwise try to find the account:
|
||||
// or try to find the account:
|
||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||
if (null === $search) {
|
||||
$this->destError = (string)trans('validation.transfer_dest_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
||||
|
@ -41,15 +41,13 @@ trait ValidatesBulkTransactionQuery
|
||||
protected function validateTransactionQuery(Validator $validator): void
|
||||
{
|
||||
$data = $validator->getData();
|
||||
// assumption is all validation has already taken place
|
||||
// and the query key exists.
|
||||
// assumption is all validation has already taken place and the query key exists.
|
||||
$json = json_decode($data['query'], true, 8, JSON_THROW_ON_ERROR);
|
||||
|
||||
if (array_key_exists('account_id', $json['where'])
|
||||
&& array_key_exists('account_id', $json['update'])
|
||||
) {
|
||||
// find both accounts
|
||||
// must be same type.
|
||||
// find both accounts, must be same type.
|
||||
// already validated: belongs to this user.
|
||||
$repository = app(AccountRepositoryInterface::class);
|
||||
$source = $repository->find((int)$json['where']['account_id']);
|
||||
|
21
changelog.md
21
changelog.md
@ -2,7 +2,26 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 5.6.2 - 2021-10-xx
|
||||
## 5.6.3 - 2021-11-12
|
||||
|
||||
### Changed
|
||||
- [Issue 5133](https://github.com/firefly-iii/firefly-iii/issues/5133) Several possible fixes for LDAP filter.
|
||||
|
||||
### Fixed
|
||||
- [Issue 5116](https://github.com/firefly-iii/firefly-iii/issues/5116) Fix missing icon
|
||||
- [Issue 5173](https://github.com/firefly-iii/firefly-iii/issues/5173) Firefly III would email too often about logins
|
||||
- [Issue 5178](https://github.com/firefly-iii/firefly-iii/issues/5178) Fix export parameter
|
||||
- [Issue 5179](https://github.com/firefly-iii/firefly-iii/issues/5179) Possible fix for issue with empty date strings
|
||||
- [Issue 5196](https://github.com/firefly-iii/firefly-iii/issues/5196) Fix issue with foreign amount formatting
|
||||
- [Issue 5200](https://github.com/firefly-iii/firefly-iii/issues/5200) Link bill to recurring transaction
|
||||
- [Issue 5218](https://github.com/firefly-iii/firefly-iii/issues/5218) Error in search
|
||||
- [Issue 5226](https://github.com/firefly-iii/firefly-iii/issues/5226) Could submit negative "skip" values
|
||||
- [Issue 5229](https://github.com/firefly-iii/firefly-iii/issues/5229) Fix serviceworker registration
|
||||
|
||||
### Security
|
||||
- Logout is now POST and other minor CSRF issues.
|
||||
|
||||
## 5.6.2 - 2021-10-09
|
||||
|
||||
### Added
|
||||
- `/health` will return `200 OK` if Firefly III is up and running, thanks @ajgon!
|
||||
|
@ -91,25 +91,25 @@
|
||||
"doctrine/dbal": "3.*",
|
||||
"fideloper/proxy": "4.*",
|
||||
"gdbots/query-parser": "^2.0",
|
||||
"guzzlehttp/guzzle": "^7.2",
|
||||
"guzzlehttp/guzzle": "^7.4",
|
||||
"jc5/google2fa-laravel": "2.0.6",
|
||||
"jc5/recovery": "^2",
|
||||
"laravel/framework": "^8.51",
|
||||
"laravel/framework": "^8.69",
|
||||
"laravel/passport": "10.*",
|
||||
"laravel/ui": "^3.0",
|
||||
"laravel/ui": "^3.3",
|
||||
"laravelcollective/html": "6.*",
|
||||
"league/commonmark": "2.*",
|
||||
"league/csv": "^9.6",
|
||||
"league/fractal": "0.*",
|
||||
"pragmarx/google2fa": "^8.0",
|
||||
"predis/predis": "^1.1",
|
||||
"psr/log": "<3",
|
||||
"ramsey/uuid": "^4.2",
|
||||
"rcrowe/twigbridge": "^0.12.1",
|
||||
"spatie/data-transfer-object": "^3.1",
|
||||
"psr/log": "<2"
|
||||
"spatie/data-transfer-object": "^3.1"
|
||||
},
|
||||
"require-dev": {
|
||||
"barryvdh/laravel-debugbar": "^3.3",
|
||||
"barryvdh/laravel-debugbar": "^3.6",
|
||||
"barryvdh/laravel-ide-helper": "2.*",
|
||||
"filp/whoops": "2.*",
|
||||
"fakerphp/faker": "1.*",
|
||||
|
545
composer.lock
generated
545
composer.lock
generated
File diff suppressed because it is too large
Load Diff
@ -24,6 +24,9 @@ declare(strict_types=1);
|
||||
use FireflyIII\Ldap\AttributeHandler;
|
||||
use FireflyIII\Ldap\Rules\UserDefinedRule;
|
||||
|
||||
$openLDAP = class_exists(LdapRecord\Models\OpenLDAP\User::class) ? LdapRecord\Models\OpenLDAP\User::class : '';
|
||||
$activeDirectory = class_exists(LdapRecord\Models\ActiveDirectory\User::class) ? LdapRecord\Models\ActiveDirectory\User::class : '';
|
||||
|
||||
return [
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
@ -108,8 +111,7 @@ return [
|
||||
|
||||
'ldap' => [
|
||||
'driver' => 'ldap',
|
||||
//'model' => LdapRecord\Models\ActiveDirectory\User::class,
|
||||
'model' => LdapRecord\Models\OpenLDAP\User::class,
|
||||
'model' => env('LDAP_DIALECT') === 'OpenLDAP' ? $openLDAP : $activeDirectory,
|
||||
'rules' => [
|
||||
UserDefinedRule::class,
|
||||
],
|
||||
|
@ -101,7 +101,7 @@ return [
|
||||
'webhooks' => true,
|
||||
'handle_debts' => true,
|
||||
],
|
||||
'version' => '5.6.2',
|
||||
'version' => '5.6.3',
|
||||
'api_version' => '1.5.4',
|
||||
'db_version' => 18,
|
||||
|
||||
|
@ -36,8 +36,8 @@ return [
|
||||
*/
|
||||
|
||||
'default' => env('LDAP_CONNECTION', 'default'),
|
||||
|
||||
'group_filter' => env('LDAP_GROUP_FILTER'),
|
||||
'extra_filter' => env('LDAP_EXTRA_FILTER'),
|
||||
'dialect' => env('LDAP_DIALECT'),
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
@ -15,15 +15,15 @@
|
||||
"laravel-mix": "^6",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"postcss": "^8.1.14",
|
||||
"postcss": "^8.3.11",
|
||||
"resolve-url-loader": "^4.0.0",
|
||||
"sass": "^1.39.2",
|
||||
"sass-loader": "^12.0.0",
|
||||
"vue-i18n": "^8.24.2",
|
||||
"sass": "^1.43.3",
|
||||
"sass-loader": "^12.2.0",
|
||||
"vue-i18n": "^8.26.7",
|
||||
"vue-loader": "^15",
|
||||
"vue-template-compiler": "^2.6.12",
|
||||
"vuex": "^3.6.2",
|
||||
"webpack": "^5.52.1"
|
||||
"webpack": "^5.62.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||
@ -32,7 +32,7 @@
|
||||
"axios-cache-adapter": "^2.7.3",
|
||||
"bootstrap": "^4.6.0",
|
||||
"bootstrap-vue": "^2.21.2",
|
||||
"chart.js": "^3.4.0",
|
||||
"chart.js": "^3.6.0",
|
||||
"icheck-bootstrap": "^3.0.1",
|
||||
"jquery-ui": "^1.12.1",
|
||||
"leaflet": "^1.7.1",
|
||||
@ -40,7 +40,7 @@
|
||||
"localforage-memoryStorageDriver": "^0.9.2",
|
||||
"overlayscrollbars": "^1.13.1",
|
||||
"sortablejs": "^1.14.0",
|
||||
"uiv": "^1.3.1",
|
||||
"uiv": "^1.4.1",
|
||||
"v-calendar": "^2.3.2",
|
||||
"vue-typeahead-bootstrap": "^2.8.0",
|
||||
"vue2-leaflet": "^2.7.1"
|
||||
|
@ -97,7 +97,7 @@
|
||||
|
||||
<GenericLocation :disabled="submitting" v-model="location" :title="$t('form.location')" :errors="errors.location"
|
||||
v-on:set-field="storeField($event)"/>
|
||||
|
||||
<!-- attachments -->
|
||||
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
|
||||
v-on:selected-attachments="selectedAttachments($event)"
|
||||
v-on:selected-no-attachments="selectedNoAttachments($event)"
|
||||
@ -275,6 +275,8 @@ export default {
|
||||
this.hasAttachments = false;
|
||||
},
|
||||
uploadedAttachments: function (e) {
|
||||
console.log('Response to event uploaded-attachments');
|
||||
console.log(e);
|
||||
this.finishSubmission();
|
||||
},
|
||||
submitForm: function (e) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
||||
<!-- Custom Tabs -->
|
||||
<!--
|
||||
<div class="card">
|
||||
<div class="card-header d-flex p-0">
|
||||
<h3 class="card-title p-3">Tabs</h3>
|
||||
@ -31,36 +32,34 @@
|
||||
<li class="nav-item"><a class="nav-link" href="#budgets" data-toggle="tab">Budgets</a></li>
|
||||
<li class="nav-item"><a class="nav-link" href="#categories" data-toggle="tab">Categories</a></li>
|
||||
</ul>
|
||||
</div><!-- /.card-header -->
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane active" id="main_chart">
|
||||
1: main chart
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
<div class="tab-pane" id="budgets">
|
||||
2: tree map from/to budget
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
<div class="tab-pane" id="categories">
|
||||
2: tree map from/to cat
|
||||
</div>
|
||||
<!-- /.tab-pane -->
|
||||
</div>
|
||||
<!-- /.tab-content -->
|
||||
</div><!-- /.card-body -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- ./card -->
|
||||
-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<TransactionListLarge
|
||||
:entries="rawTransactions"
|
||||
:page="currentPage"
|
||||
ref="list"
|
||||
:total="total"
|
||||
:per-page="perPage"
|
||||
:sort-desc="sortDesc"
|
||||
v-on:jump-page="jumpToPage($event)"
|
||||
v-on:refreshed-cache-key="refreshedKey"
|
||||
/>
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
||||
@ -106,7 +105,7 @@ export default {
|
||||
perPage: 51,
|
||||
locale: 'en-US',
|
||||
api: null,
|
||||
nameLoading:false
|
||||
nameLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
@ -132,16 +131,23 @@ export default {
|
||||
.then(response => {
|
||||
let start = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.start);
|
||||
let end = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.end);
|
||||
document.getElementById('page-subTitle').innerText = this.$t('firefly.journals_in_period_for_account_js', {start: start, end: end, title: response.data.data.attributes.name});
|
||||
document.getElementById('page-subTitle').innerText = this.$t('firefly.journals_in_period_for_account_js', {
|
||||
start: start,
|
||||
end: end,
|
||||
title: response.data.data.attributes.name
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
},
|
||||
refreshedKey: function () {
|
||||
this.loading = false;
|
||||
this.getTransactions();
|
||||
this.updatePageTitle();
|
||||
},
|
||||
getTransactions: function () {
|
||||
if (this.showReady && !this.loading) {
|
||||
|
||||
|
||||
this.loading = true;
|
||||
configureAxios().then(async (api) => {
|
||||
// console.log('Now getTransactions() x Start');
|
||||
@ -149,8 +155,7 @@ export default {
|
||||
let endStr = format(this.end, 'y-MM-dd');
|
||||
this.rawTransactions = [];
|
||||
|
||||
let url = './api/v1/accounts/' + this.accountId + '/transactions?page=1&limit=' + this.perPage + '&start=' + startStr + '&end=' + endStr;
|
||||
|
||||
let url = './api/v1/accounts/' + this.accountId + '/transactions?page=1&limit=' + this.perPage + '&start=' + startStr + '&end=' + endStr + '&cache=' + this.cacheKey;
|
||||
|
||||
api.get(url)
|
||||
.then(response => {
|
||||
|
@ -30,7 +30,6 @@
|
||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success" role="progressbar">
|
||||
<span v-if="budgetLimit.pctGreen > 35">
|
||||
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
|
||||
<!-- -->
|
||||
</span>
|
||||
|
||||
|
||||
@ -45,7 +44,7 @@
|
||||
|
||||
<div :aria-valuenow="budgetLimit.pctRed" :style="'width: '+ budgetLimit.pctRed + '%;'"
|
||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-danger" role="progressbar">
|
||||
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35" class="text-muted">
|
||||
<span v-if="budgetLimit.pctOrange <= 50 && budgetLimit.pctRed > 35" class="text-white">
|
||||
{{ $t('firefly.spent_x_of_y', {amount: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.spent), total: Intl.NumberFormat(locale, {style: 'currency', currency: budgetLimit.currency_code}).format(budgetLimit.amount)}) }}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -24,6 +24,7 @@
|
||||
<td style="width:25%;">
|
||||
<a :href="'./budgets/show/' + budget.id">{{ budget.name }}</a>
|
||||
</td>
|
||||
<td> </td>
|
||||
<td class="align-middle text-right">
|
||||
<span class="text-danger">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budget.currency_code}).format(parseFloat(budget.spent)) }}
|
||||
|
@ -214,8 +214,8 @@ export default {
|
||||
let pctGreen = 0;
|
||||
let pctOrange = 0;
|
||||
let pctRed = 0;
|
||||
//console.log('Collected "' + period + '" budget limit #' + currentId + ' (part of budget #' + budgetId + ')');
|
||||
//console.log('Spent ' + spentFloat + ' of ' + amount);
|
||||
console.log('Collected "' + period + '" budget limit #' + currentId + ' (part of budget #' + budgetId + ')');
|
||||
console.log('Spent ' + spentFloatPos + ' of ' + amount);
|
||||
|
||||
// remove budget info from rawBudgets if it's there:
|
||||
this.filterBudgets(budgetId, currencyId);
|
||||
@ -230,6 +230,12 @@ export default {
|
||||
pctOrange = (spentFloatPos / amount) * 100;
|
||||
pctRed = 100 - pctOrange;
|
||||
}
|
||||
// spent exactly on budget
|
||||
if (0.0 !== spentFloatPos && spentFloatPos === amount) {
|
||||
pctOrange = 0;
|
||||
pctRed = 100;
|
||||
}
|
||||
|
||||
let obj = {
|
||||
id: currentId,
|
||||
amount: current.attributes.amount,
|
||||
|
@ -97,22 +97,22 @@ export default {
|
||||
// });
|
||||
|
||||
// new code
|
||||
// console.log('start of new');
|
||||
console.log('start of new');
|
||||
let files = this.$refs.att.files;
|
||||
this.uploads = files.length;
|
||||
// loop all files and create attachments.
|
||||
for (let i in files) {
|
||||
if (files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
// console.log('Now at file ' + (parseInt(i) + 1) + ' / ' + files.length);
|
||||
console.log('Now at file ' + (parseInt(i) + 1) + ' / ' + files.length);
|
||||
// read file into file reader:
|
||||
let current = files[i];
|
||||
let fileReader = new FileReader();
|
||||
let theParent = this; // dont ask me why i need to do this.
|
||||
fileReader.onloadend = evt => {
|
||||
if (evt.target.readyState === FileReader.DONE) {
|
||||
// console.log('I am done reading file ' + (parseInt(i) + 1));
|
||||
console.log('I am done reading file ' + (parseInt(i) + 1));
|
||||
this.createAttachment(current.name).then(response => {
|
||||
// console.log('Created attachment. Now upload (1)');
|
||||
console.log('Created attachment. Now upload (1)');
|
||||
return theParent.uploadAttachment(response.data.data.id, new Blob([evt.target.result]));
|
||||
}).then(theParent.countAttachment);
|
||||
}
|
||||
@ -121,7 +121,7 @@ export default {
|
||||
}
|
||||
}
|
||||
if (0 === files.length) {
|
||||
// console.log('No files to upload. Emit event!');
|
||||
console.log('No files to upload. Emit event!');
|
||||
this.$emit('uploaded-attachments', this.transaction_journal_id);
|
||||
}
|
||||
// Promise.all(promises).then(response => {
|
||||
@ -138,15 +138,15 @@ export default {
|
||||
methods: {
|
||||
countAttachment: function () {
|
||||
this.uploaded++;
|
||||
// console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
|
||||
console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
|
||||
if (this.uploaded >= this.uploads) {
|
||||
// console.log('All files uploaded. Emit event for ' + this.transaction_journal_id + '(' + this.index + ')');
|
||||
this.$emit('uploaded-attachments', this.transaction_journal_id);
|
||||
console.log('All files uploaded. Emit event for ' + this.uploadObjectId);
|
||||
this.$emit('uploaded-attachments', this.uploadObjectId);
|
||||
}
|
||||
},
|
||||
uploadAttachment: function (attachmentId, data) {
|
||||
this.created++;
|
||||
// console.log('Now in uploadAttachment()');
|
||||
console.log('Now in uploadAttachment()');
|
||||
const uploadUri = './api/v1/attachments/' + attachmentId + '/upload';
|
||||
return axios.post(uploadUri, data)
|
||||
},
|
||||
|
@ -31,6 +31,7 @@
|
||||
:count="transactions.length"
|
||||
:custom-fields="customFields"
|
||||
:date="date"
|
||||
ref="splitForms"
|
||||
:destination-allowed-types="destinationAllowedTypes"
|
||||
:index="index"
|
||||
:source-allowed-types="sourceAllowedTypes"
|
||||
@ -70,7 +71,7 @@
|
||||
|
||||
</div>
|
||||
<button type="button" class="btn btn-outline-primary btn-block" @click="addTransactionArray"><span class="far fa-clone"></span> {{
|
||||
$t('firefly.add_another_split')
|
||||
$t('firefly.add_another_split')
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
@ -333,7 +334,6 @@ export default {
|
||||
for (let i in this.transactions) {
|
||||
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||
if (this.transactions.hasOwnProperty(i)) {
|
||||
//this.
|
||||
// console.log('Reset attachment #' + i);
|
||||
this.updateField({index: i, field: 'transaction_journal_id', value: 0});
|
||||
this.updateField({index: i, field: 'errors', value: this.defaultErrors})
|
||||
@ -344,7 +344,8 @@ export default {
|
||||
// reset the form:
|
||||
if (this.resetFormAfter) {
|
||||
this.resetTransactions();
|
||||
this.addTransaction();
|
||||
setTimeout(this.addTransaction, 50);
|
||||
|
||||
}
|
||||
return Promise.resolve({response: 'from finaliseSubmission'});
|
||||
},
|
||||
@ -433,6 +434,12 @@ export default {
|
||||
this.updateField({index: payload.index, field: payload.direction + '_account_currency_symbol', value: payload.currency_symbol});
|
||||
|
||||
//this.calculateTransactionType(payload.index);
|
||||
if ('source' === payload.direction && true === payload.user_selected) {
|
||||
this.$refs.splitForms[payload.index].$refs.destinationAccount.giveFocus();
|
||||
}
|
||||
if ('destination' === payload.direction && true === payload.user_selected) {
|
||||
this.$refs.splitForms[payload.index].$refs.amount.giveFocus();
|
||||
}
|
||||
},
|
||||
storeField: function (payload) {
|
||||
this.updateField(payload);
|
||||
|
@ -20,6 +20,7 @@
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!--
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<div class="card">
|
||||
@ -36,6 +37,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
<!-- page is ignored for the time being -->
|
||||
<TransactionListLarge
|
||||
:entries="rawTransactions"
|
||||
@ -44,7 +46,9 @@
|
||||
:per-page="perPage"
|
||||
:sort-desc="sortDesc"
|
||||
v-on:jump-page="jumpToPage($event)"
|
||||
v-on:refreshed-cache-key="refreshedKey"
|
||||
/>
|
||||
<!--
|
||||
<div class="row">
|
||||
<div class="col-xl-2 col-lg-4 col-sm-6 col-xs-12" v-for="range in ranges">
|
||||
<div class="card">
|
||||
@ -56,7 +60,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -141,13 +147,12 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
...mapMutations('root', ['refreshCacheKey',]),
|
||||
newCacheKey: function () {
|
||||
this.refreshCacheKey();
|
||||
refreshedKey: function () {
|
||||
this.downloaded = false;
|
||||
this.accounts = [];
|
||||
this.rawTransactions = [];
|
||||
this.getTransactionList();
|
||||
},
|
||||
jumpToPage: function(event) {
|
||||
jumpToPage: function (event) {
|
||||
// console.log('noticed a change!');
|
||||
this.currentPage = event.page;
|
||||
this.downloadTransactionList(event.page);
|
||||
|
@ -58,6 +58,8 @@
|
||||
:source-allowed-types="sourceAllowedTypes"
|
||||
:transaction-type="transactionType"
|
||||
direction="source"
|
||||
ref="sourceAccount"
|
||||
v-on:selected-account="triggerNextAccount($event)"
|
||||
/>
|
||||
</div>
|
||||
<!-- switcharoo! -->
|
||||
@ -79,6 +81,7 @@
|
||||
:destination-allowed-types="destinationAllowedTypes"
|
||||
:errors="transaction.errors.destination"
|
||||
:index="index"
|
||||
ref="destinationAccount"
|
||||
:transaction-type="transactionType"
|
||||
:source-allowed-types="sourceAllowedTypes"
|
||||
direction="destination"
|
||||
@ -93,6 +96,7 @@
|
||||
<!-- AMOUNT -->
|
||||
<TransactionAmount
|
||||
v-on="$listeners"
|
||||
ref="amount"
|
||||
:amount="transaction.amount"
|
||||
:destination-currency-symbol="this.transaction.destination_account_currency_symbol"
|
||||
:errors="transaction.errors.amount"
|
||||
@ -379,6 +383,13 @@ export default {
|
||||
// console.log('Will remove transaction ' + this.index);
|
||||
this.$emit('remove-transaction', {index: this.index});
|
||||
},
|
||||
triggerNextAccount: function(e) {
|
||||
//alert(e);
|
||||
if('source' === e) {
|
||||
console.log('Jump to destination!');
|
||||
this.$refs.destinationAccount.giveFocus();
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
splitDate: function () {
|
||||
|
@ -37,6 +37,7 @@
|
||||
:placeholder="$t('firefly.' + direction + '_account')"
|
||||
:serializer="item => item.name_with_balance"
|
||||
:showOnFocus=true
|
||||
ref="inputThing"
|
||||
aria-autocomplete="none"
|
||||
autocomplete="off"
|
||||
@hit="userSelectedAccount"
|
||||
@ -119,6 +120,12 @@ export default {
|
||||
getACURL: function (types, query) {
|
||||
return './api/v1/autocomplete/accounts?types=' + types.join(',') + '&query=' + query;
|
||||
},
|
||||
giveFocus: function() {
|
||||
console.log('I want focus! now OK: ' + this.direction + ' l: ' + this.accounts.length);
|
||||
//console.log(this.$refs.inputThing.$refs.input.value);
|
||||
this.$refs.inputThing.$refs.input.focus();
|
||||
console.log(this.$refs.inputThing.isFocused);
|
||||
},
|
||||
userSelectedAccount: function (event) {
|
||||
// console.log('userSelectedAccount!');
|
||||
// console.log('To prevent invalid propogation, set selectedAccountTrigger = true');
|
||||
@ -205,10 +212,14 @@ export default {
|
||||
currency_id: value.currency_id,
|
||||
currency_code: value.currency_code,
|
||||
currency_symbol: value.currency_symbol,
|
||||
user_selected: true,
|
||||
}
|
||||
// jump to next field somehow.
|
||||
|
||||
);
|
||||
//console.log('watch::selectedAccount() will now set accountName because selectedAccountTrigger = true');
|
||||
this.accountName = value.name;
|
||||
|
||||
}
|
||||
if (false === this.selectedAccountTrigger) {
|
||||
//console.log('watch::selectedAccount() will NOT set accountName because selectedAccountTrigger = false');
|
||||
@ -238,6 +249,7 @@ export default {
|
||||
currency_id: null,
|
||||
currency_code: null,
|
||||
currency_symbol: null,
|
||||
user_selected: false
|
||||
}
|
||||
);
|
||||
// this.account = {name: value, type: null, id: null, currency_id: null, currency_code: null, currency_symbol: null};
|
||||
|
@ -33,6 +33,7 @@
|
||||
autocomplete="off"
|
||||
name="amount[]"
|
||||
type="number"
|
||||
ref="input"
|
||||
step="any"
|
||||
>
|
||||
</div>
|
||||
@ -71,7 +72,10 @@ export default {
|
||||
methods: {
|
||||
formatNumber(str) {
|
||||
return parseFloat(str).toFixed(this.fractionDigits);
|
||||
}
|
||||
},
|
||||
giveFocus: function() {
|
||||
this.$refs.input.focus();
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -53,9 +53,9 @@
|
||||
</template>
|
||||
<template #cell(type)="data">
|
||||
<span v-if="!data.item.dummy">
|
||||
<span class="fas fa-long-arrow-alt-right" v-if="'deposit' === data.item.type"></span>
|
||||
<span class="fas fa-long-arrow-alt-left" v-else-if="'withdrawal' === data.item.type"></span>
|
||||
<span class="fas fa-long-arrows-alt-h" v-else-if="'transfer' === data.item.type"></span>
|
||||
<span class="fas fa-long-arrow-alt-right" v-if="'deposit' === data.item.type.toLowerCase()"></span>
|
||||
<span class="fas fa-long-arrow-alt-left" v-if="'withdrawal' === data.item.type.toLowerCase()"></span>
|
||||
<span class="fas fa-arrows-alt-h" v-if="'transfer' === data.item.type.toLowerCase()"></span>
|
||||
</span>
|
||||
</template>
|
||||
<template #cell(description)="data">
|
||||
@ -86,7 +86,7 @@
|
||||
<span :class="'text-danger ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'withdrawal' === data.item.type">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(-data.item.amount) }}
|
||||
</span>
|
||||
<span :class="'text-muted ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'transfer' === data.item.type">
|
||||
<span :class="'text-muted ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'transfer' === data.item.type.toLowerCase()">
|
||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount) }}
|
||||
</span>
|
||||
<br />
|
||||
@ -220,21 +220,26 @@ export default {
|
||||
this.$emit('jump-page', {page: value});
|
||||
},
|
||||
entries: function (value) {
|
||||
console.log('detected new transactions!');
|
||||
this.parseTransactions();
|
||||
},
|
||||
value: function(value) {
|
||||
console.log('Watch value!');
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
...mapMutations('root', ['refreshCacheKey',]),
|
||||
parseTransactions: function () {
|
||||
this.transactions = [];
|
||||
// console.log('Start of parseTransactions. Count of entries is ' + this.entries.length + ' and page is ' + this.page);
|
||||
// console.log('Reported total is ' + this.total);
|
||||
if (0 === this.entries.length) {
|
||||
// console.log('Will not render now');
|
||||
console.log('Will not render now');
|
||||
return;
|
||||
}
|
||||
// console.log('Now have ' + this.transactions.length + ' transactions');
|
||||
console.log('Now have ' + this.transactions.length + ' transactions');
|
||||
for (let i = 0; i < this.total; i++) {
|
||||
this.transactions.push({dummy: true});
|
||||
this.transactions.push({dummy: true,type: 'x'});
|
||||
// console.log('Push dummy to index ' + i);
|
||||
// console.log('Now have ' + this.transactions.length + ' transactions');
|
||||
}
|
||||
@ -259,8 +264,9 @@ export default {
|
||||
this.loading = false;
|
||||
},
|
||||
newCacheKey: function () {
|
||||
alert('TODO');
|
||||
this.refreshCacheKey();
|
||||
console.log('Cache key is now ' + this.cacheKey);
|
||||
this.$emit('refreshed-cache-key');
|
||||
},
|
||||
updateFieldList: function () {
|
||||
this.fields = [
|
||||
@ -314,6 +320,7 @@ export default {
|
||||
let info = transaction.attributes.transactions[i];
|
||||
let split = {};
|
||||
row.amount = row.amount + parseFloat(info.amount);
|
||||
split.type = info.type;
|
||||
split.description = info.description;
|
||||
split.amount = info.amount;
|
||||
split.currency_code = info.currency_code;
|
||||
|
@ -70,7 +70,7 @@
|
||||
"daily_budgets": "Denn\u00ed rozpo\u010dty",
|
||||
"weekly_budgets": "T\u00fddenn\u00ed rozpo\u010dty",
|
||||
"monthly_budgets": "M\u011bs\u00ed\u010dn\u00ed rozpo\u010dty",
|
||||
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
|
||||
"journals_in_period_for_account_js": "Ve\u0161ker\u00e9 transakce pro \u00fa\u010det {title} mezi {start} a {end}",
|
||||
"quarterly_budgets": "\u010ctvrtletn\u00ed rozpo\u010dty",
|
||||
"create_new_expense": "Vytvo\u0159it v\u00fddajov\u00fd \u00fa\u010det",
|
||||
"create_new_revenue": "Vytvo\u0159it nov\u00fd p\u0159\u00edjmov\u00fd \u00fa\u010det",
|
||||
|
@ -70,7 +70,7 @@
|
||||
"daily_budgets": "Presupuestos diarios",
|
||||
"weekly_budgets": "Presupuestos semanales",
|
||||
"monthly_budgets": "Presupuestos mensuales",
|
||||
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
|
||||
"journals_in_period_for_account_js": "Todas las transacciones de la cuenta {title} entre {start} y {end}",
|
||||
"quarterly_budgets": "Presupuestos trimestrales",
|
||||
"create_new_expense": "Crear nueva cuenta de gastos",
|
||||
"create_new_revenue": "Crear nueva cuenta de ingresos",
|
||||
@ -142,27 +142,27 @@
|
||||
"transaction_expand_split": "Expandir divisi\u00f3n",
|
||||
"transaction_collapse_split": "Colapsar divisi\u00f3n",
|
||||
"default_group_title_name": "(sin agrupaci\u00f3n)",
|
||||
"bill_repeats_weekly": "Repeats weekly",
|
||||
"bill_repeats_monthly": "Repeats monthly",
|
||||
"bill_repeats_quarterly": "Repeats quarterly",
|
||||
"bill_repeats_half-year": "Repeats every half year",
|
||||
"bill_repeats_yearly": "Repeats yearly",
|
||||
"bill_repeats_weekly_other": "Repeats every other week",
|
||||
"bill_repeats_monthly_other": "Repeats every other month",
|
||||
"bill_repeats_quarterly_other": "Repeats every other quarter",
|
||||
"bill_repeats_half-year_other": "Repeats yearly",
|
||||
"bill_repeats_yearly_other": "Repeats every other year",
|
||||
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
|
||||
"bill_repeats_monthly_skip": "Repeats every {skip} months",
|
||||
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
|
||||
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
|
||||
"bill_repeats_yearly_skip": "Repeats every {skip} years",
|
||||
"bill_repeats_weekly": "Repetir semanalmente",
|
||||
"bill_repeats_monthly": "Repetir mensualmente",
|
||||
"bill_repeats_quarterly": "Repite trimestralmente",
|
||||
"bill_repeats_half-year": "Repetir cada 6 meses",
|
||||
"bill_repeats_yearly": "Repetir anualmente",
|
||||
"bill_repeats_weekly_other": "Repetir cada dos semanas",
|
||||
"bill_repeats_monthly_other": "Repetir cada dos meses",
|
||||
"bill_repeats_quarterly_other": "Repetir cada dos trimestres",
|
||||
"bill_repeats_half-year_other": "Repetir anualmente",
|
||||
"bill_repeats_yearly_other": "Repetir cada dos a\u00f1os",
|
||||
"bill_repeats_weekly_skip": "Repetir cada {skip} semanas",
|
||||
"bill_repeats_monthly_skip": "Repetir cada {skip} meses",
|
||||
"bill_repeats_quarterly_skip": "Repetir cada {skip} trimestres",
|
||||
"bill_repeats_half-year_skip": "Repetir cada {skip} medios a\u00f1os",
|
||||
"bill_repeats_yearly_skip": "Repetir cada {skip} a\u00f1os",
|
||||
"not_expected_period": "No se espera en este per\u00edodo",
|
||||
"subscriptions": "Subscriptions",
|
||||
"subscriptions": "Suscripciones",
|
||||
"bill_expected_date_js": "Expected {date}",
|
||||
"inactive": "Inactivo",
|
||||
"forever": "Forever",
|
||||
"extension_date_is": "Extension date is {date}",
|
||||
"forever": "Siempre",
|
||||
"extension_date_is": "Fecha de extensi\u00f3n es {date}",
|
||||
"create_new_bill": "Crear nueva factura",
|
||||
"store_new_bill": "Crear factura",
|
||||
"repeat_freq_yearly": "anualmente",
|
||||
@ -170,13 +170,13 @@
|
||||
"repeat_freq_quarterly": "trimestralmente",
|
||||
"repeat_freq_monthly": "mensualmente",
|
||||
"repeat_freq_weekly": "semanalmente",
|
||||
"credit_card_type_monthlyFull": "Full payment every month",
|
||||
"credit_card_type_monthlyFull": "Pago completo cada mes",
|
||||
"update_liabilities_account": "Actualizar pasivo",
|
||||
"update_expense_account": "Actualizar cuenta de gastos",
|
||||
"update_revenue_account": "Actualizar cuenta de ingresos",
|
||||
"update_undefined_account": "Update account",
|
||||
"update_undefined_account": "Actualizar cuenta",
|
||||
"update_asset_account": "Actualizar cuenta de activos",
|
||||
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
|
||||
"updated_account_js": "Cuenta actualizada \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
|
||||
},
|
||||
"list": {
|
||||
"piggy_bank": "Alcancilla",
|
||||
@ -197,10 +197,10 @@
|
||||
"liability_direction": "Pasivo entrada\/salida",
|
||||
"currentBalance": "Balance actual",
|
||||
"next_expected_match": "Pr\u00f3xima coincidencia esperada",
|
||||
"expected_info": "Next expected transaction",
|
||||
"start_date": "Start date",
|
||||
"end_date": "End date",
|
||||
"payment_info": "Payment information"
|
||||
"expected_info": "Siguiente transacci\u00f3n esperada",
|
||||
"start_date": "Fecha de inicio",
|
||||
"end_date": "Fecha fin",
|
||||
"payment_info": "Informaci\u00f3n del pago"
|
||||
},
|
||||
"config": {
|
||||
"html_language": "es",
|
||||
@ -221,7 +221,7 @@
|
||||
"repeat_freq": "Repetici\u00f3n",
|
||||
"skip": "Saltar",
|
||||
"startdate": "Fecha de inicio",
|
||||
"enddate": "End date",
|
||||
"enddate": "Fecha fin",
|
||||
"object_group": "Grupo",
|
||||
"attachments": "Adjuntos",
|
||||
"active": "Activo",
|
||||
@ -252,6 +252,6 @@
|
||||
"amount_max": "Importe m\u00e1ximo",
|
||||
"start_date": "Inicio del rango",
|
||||
"end_date": "Final del rango",
|
||||
"extension_date": "Extension date"
|
||||
"extension_date": "Fecha de extensi\u00f3n"
|
||||
}
|
||||
}
|
@ -70,7 +70,7 @@
|
||||
"daily_budgets": "Dagliga budgetar",
|
||||
"weekly_budgets": "Veckovis budgetar",
|
||||
"monthly_budgets": "M\u00e5natliga budgetar",
|
||||
"journals_in_period_for_account_js": "All transactions for account {title} between {start} and {end}",
|
||||
"journals_in_period_for_account_js": "Alla transaktioner f\u00f6r konto {title} mellan {start} och {end}",
|
||||
"quarterly_budgets": "Kvartalsbudgetar",
|
||||
"create_new_expense": "Skapa ett nytt utgiftskonto",
|
||||
"create_new_revenue": "Skapa ett nytt int\u00e4ktskonto",
|
||||
@ -174,9 +174,9 @@
|
||||
"update_liabilities_account": "Uppdatera skuld",
|
||||
"update_expense_account": "Uppdatera utgiftskonto",
|
||||
"update_revenue_account": "Uppdatera int\u00e4ktskonto",
|
||||
"update_undefined_account": "Update account",
|
||||
"update_undefined_account": "Uppdatera konto",
|
||||
"update_asset_account": "Uppdatera tillg\u00e5ngskonto",
|
||||
"updated_account_js": "Updated account \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
|
||||
"updated_account_js": "Uppdaterade kontot \"<a href=\"accounts\/show\/{ID}\">{title}<\/a>\"."
|
||||
},
|
||||
"list": {
|
||||
"piggy_bank": "Spargris",
|
||||
|
1725
frontend/yarn.lock
1725
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
@ -10,17 +10,17 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@johmun/vue-tags-input": "^2",
|
||||
"@vue/compiler-sfc": "^3.2.19",
|
||||
"axios": "^0.22",
|
||||
"@vue/compiler-sfc": "^3.2.21",
|
||||
"axios": "^0.24",
|
||||
"bootstrap-sass": "^3",
|
||||
"cross-env": "^7.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"jquery": "^3",
|
||||
"laravel-mix": "^6.0",
|
||||
"postcss": "^8.3",
|
||||
"uiv": "^1.3",
|
||||
"uiv": "^1.4",
|
||||
"vue": "^2.6",
|
||||
"vue-i18n": "^8.25",
|
||||
"vue-i18n": "^8.26",
|
||||
"vue-loader": "^15",
|
||||
"vue-template-compiler": "^2.6"
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -59,6 +59,32 @@ function readCookie(name) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function moveRuleGroup(e) {
|
||||
let box = $(e.currentTarget);
|
||||
var direction = box.data('direction');
|
||||
var groupId = box.data('id');
|
||||
|
||||
$.post(moveRuleGroupUrl, {_token: token, direction: direction, id: groupId}).then(function () {
|
||||
location.reload();
|
||||
}).fail(function() {
|
||||
alert('I failed :(');
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function duplicateRule(e) {
|
||||
let box = $(e.currentTarget);
|
||||
var ruleId = box.data('id');
|
||||
$.post(duplicateRuleUrl, {_token: token, id: ruleId}).then(function () {
|
||||
location.reload();
|
||||
}).fail(function() {
|
||||
alert('I failed :(');
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$(function () {
|
||||
"use strict";
|
||||
@ -71,6 +97,9 @@ $(function () {
|
||||
}
|
||||
);
|
||||
|
||||
$('.move-group').click(moveRuleGroup);
|
||||
$('.duplicate-rule').click(duplicateRule);
|
||||
|
||||
$('.rules-box').each(function (i, v) {
|
||||
var box = $(v);
|
||||
var groupId = box.data('group');
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user