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
|
# https://docs.firefly-iii.org/advanced-installation/authentication
|
||||||
AUTHENTICATION_GUARD=web
|
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:
|
# LDAP connection settings:
|
||||||
#
|
#
|
||||||
@ -193,14 +199,13 @@ LDAP_PASSWORD=super_secret
|
|||||||
LDAP_AUTH_FIELD=uid
|
LDAP_AUTH_FIELD=uid
|
||||||
|
|
||||||
#
|
#
|
||||||
# If you wish to only authenticate users from a specific group, use the
|
# If you wish to only authenticate users from a specific group, use the base DN above.
|
||||||
# group filter. Leave empty or remove if not in use.
|
|
||||||
#
|
#
|
||||||
# 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
|
# 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.
|
# 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.
|
# 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.
|
# 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
|
# 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
|
# Configuration for probot-stale - https://github.com/probot/stale
|
||||||
|
|
||||||
# Number of days of inactivity before an Issue or Pull Request becomes 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.
|
# 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.
|
# 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
|
# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable
|
||||||
# - "[Status] Maybe Later"
|
# - "[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
|
// budget, category, bill and piggy
|
||||||
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
'transactions.*.budget_id' => ['mustExist:budgets,id', new BelongsUser],
|
||||||
'transactions.*.budget_name' => ['between:1,255', 'nullable', 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.*.category_name' => 'between:1,255|nullable',
|
||||||
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
'transactions.*.bill_id' => ['numeric', 'nullable', 'mustExist:bills,id', new BelongsUser],
|
||||||
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
'transactions.*.bill_name' => ['between:1,255', 'nullable', new BelongsUser],
|
||||||
|
@ -154,6 +154,7 @@ class ExportData extends Command
|
|||||||
/**
|
/**
|
||||||
* @return array
|
* @return array
|
||||||
* @throws FireflyException
|
* @throws FireflyException
|
||||||
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
private function parseOptions(): array
|
private function parseOptions(): array
|
||||||
{
|
{
|
||||||
@ -201,12 +202,17 @@ class ExportData extends Command
|
|||||||
$error = true;
|
$error = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(null === $this->option($field)) {
|
||||||
|
Log::info(sprintf('No date given in field "%s"', $field));
|
||||||
|
$error = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (true === $error && 'start' === $field) {
|
if (true === $error && 'start' === $field) {
|
||||||
$journal = $this->journalRepository->firstNull();
|
$journal = $this->journalRepository->firstNull();
|
||||||
$date = null === $journal ? Carbon::now()->subYear() : $journal->date;
|
$date = null === $journal ? Carbon::now()->subYear() : $journal->date;
|
||||||
$date->startOfDay();
|
$date->startOfDay();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (true === $error && 'end' === $field) {
|
if (true === $error && 'end' === $field) {
|
||||||
$date = today(config('app.timezone'));
|
$date = today(config('app.timezone'));
|
||||||
$date->endOfDay();
|
$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 Carbon\Carbon;
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use FireflyIII\Events\ActuallyLoggedIn;
|
||||||
use FireflyIII\Events\DetectedNewIPAddress;
|
use FireflyIII\Events\DetectedNewIPAddress;
|
||||||
use FireflyIII\Events\RegisteredUser;
|
use FireflyIII\Events\RegisteredUser;
|
||||||
use FireflyIII\Events\RequestedNewPassword;
|
use FireflyIII\Events\RequestedNewPassword;
|
||||||
@ -116,9 +117,24 @@ class UserEventHandler
|
|||||||
*/
|
*/
|
||||||
public function createGroupMembership(RegisteredUser $event): bool
|
public function createGroupMembership(RegisteredUser $event): bool
|
||||||
{
|
{
|
||||||
$user = $event->user;
|
$user = $event->user;
|
||||||
|
$groupExists = true;
|
||||||
|
$groupTitle = $user->email;
|
||||||
|
$index = 1;
|
||||||
|
|
||||||
// create a new group.
|
// 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();
|
$role = UserRole::where('title', UserRole::OWNER)->first();
|
||||||
if (null === $role) {
|
if (null === $role) {
|
||||||
throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?');
|
throw new FireflyException('The user role is unexpectedly empty. Did you run all migrations?');
|
||||||
@ -317,12 +333,11 @@ class UserEventHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Login $event
|
* @param ActuallyLoggedIn $event
|
||||||
*
|
|
||||||
* @throws FireflyException
|
|
||||||
*/
|
*/
|
||||||
public function storeUserIPAddress(Login $event): void
|
public function storeUserIPAddress(ActuallyLoggedIn $event): void
|
||||||
{
|
{
|
||||||
|
Log::debug('Now in storeUserIPAddress');
|
||||||
/** @var User $user */
|
/** @var User $user */
|
||||||
$user = $event->user;
|
$user = $event->user;
|
||||||
/** @var array $preference */
|
/** @var array $preference */
|
||||||
|
@ -79,7 +79,7 @@ class NetWorth implements NetWorthInterface
|
|||||||
|
|
||||||
$netWorth = [];
|
$netWorth = [];
|
||||||
$result = [];
|
$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
|
// get default currency
|
||||||
$default = app('amount')->getDefaultCurrencyByUser($this->user);
|
$default = app('amount')->getDefaultCurrencyByUser($this->user);
|
||||||
@ -90,16 +90,16 @@ class NetWorth implements NetWorthInterface
|
|||||||
// get the preferred currency for this account
|
// get the preferred currency for this account
|
||||||
/** @var Account $account */
|
/** @var Account $account */
|
||||||
foreach ($accounts as $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 = (int)$this->accountRepository->getMetaValue($account, 'currency_id');
|
||||||
$currencyId = 0 === $currencyId ? $default->id : $currencyId;
|
$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 in array:
|
||||||
$balance = $balances[$account->id] ?? '0';
|
$balance = $balances[$account->id] ?? '0';
|
||||||
|
|
||||||
Log::debug(sprintf('Balance is %s', $balance));
|
//Log::debug(sprintf('Balance is %s', $balance));
|
||||||
|
|
||||||
// always subtract virtual balance.
|
// always subtract virtual balance.
|
||||||
$virtualBalance = (string)$account->virtual_balance;
|
$virtualBalance = (string)$account->virtual_balance;
|
||||||
@ -107,14 +107,14 @@ class NetWorth implements NetWorthInterface
|
|||||||
$balance = bcsub($balance, $virtualBalance);
|
$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)) {
|
if (!array_key_exists($currencyId, $netWorth)) {
|
||||||
$netWorth[$currencyId] = '0';
|
$netWorth[$currencyId] = '0';
|
||||||
}
|
}
|
||||||
$netWorth[$currencyId] = bcadd($balance, $netWorth[$currencyId]);
|
$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);
|
ksort($netWorth);
|
||||||
|
|
||||||
|
@ -209,10 +209,10 @@ class PopupReport implements PopupReportInterface
|
|||||||
$collector = app(GroupCollectorInterface::class);
|
$collector = app(GroupCollectorInterface::class);
|
||||||
|
|
||||||
// set report accounts + the request accounts:
|
// set report accounts + the request accounts:
|
||||||
$set = $attributes['accounts'] ?? new Collection;
|
//$set = $attributes['accounts'] ?? new Collection;
|
||||||
$set->push($account);
|
//$set->push($account);
|
||||||
|
|
||||||
$collector->setBothAccounts($set)
|
$collector->setDestinationAccounts(new Collection([$account]))
|
||||||
->setRange($attributes['startDate'], $attributes['endDate'])
|
->setRange($attributes['startDate'], $attributes['endDate'])
|
||||||
->withAccountInformation()
|
->withAccountInformation()
|
||||||
->withBudgetInformation()
|
->withBudgetInformation()
|
||||||
@ -222,7 +222,6 @@ class PopupReport implements PopupReportInterface
|
|||||||
if (null !== $currency) {
|
if (null !== $currency) {
|
||||||
$collector->setCurrency($currency);
|
$collector->setCurrency($currency);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $collector->getExtractedJournals();
|
return $collector->getExtractedJournals();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ class IndexController extends Controller
|
|||||||
|
|
||||||
$accounts->each(
|
$accounts->each(
|
||||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
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->startBalance = $this->isInArray($startBalances, $account->id);
|
||||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||||
@ -163,7 +163,7 @@ class IndexController extends Controller
|
|||||||
$accounts->each(
|
$accounts->each(
|
||||||
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
function (Account $account) use ($activities, $startBalances, $endBalances) {
|
||||||
// See reference nr. 68
|
// 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->startBalance = $this->isInArray($startBalances, $account->id);
|
||||||
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
$account->endBalance = $this->isInArray($endBalances, $account->id);
|
||||||
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
$account->difference = bcsub($account->endBalance, $account->startBalance);
|
||||||
|
@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Auth;
|
|||||||
use Adldap;
|
use Adldap;
|
||||||
use Cookie;
|
use Cookie;
|
||||||
use DB;
|
use DB;
|
||||||
|
use FireflyIII\Events\ActuallyLoggedIn;
|
||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Http\Controllers\Controller;
|
use FireflyIII\Http\Controllers\Controller;
|
||||||
use FireflyIII\Providers\RouteServiceProvider;
|
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.
|
// 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);
|
return $this->sendLoginResponse($request);
|
||||||
}
|
}
|
||||||
Log::warning('Login attempt failed.');
|
Log::warning('Login attempt failed.');
|
||||||
|
@ -59,7 +59,6 @@ class ReportController extends Controller
|
|||||||
'category-entry' => $this->categoryEntry($attributes),
|
'category-entry' => $this->categoryEntry($attributes),
|
||||||
'budget-entry' => $this->budgetEntry($attributes),
|
'budget-entry' => $this->budgetEntry($attributes),
|
||||||
};
|
};
|
||||||
|
|
||||||
return response()->json(['html' => $html]);
|
return response()->json(['html' => $html]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -648,10 +648,9 @@ class CategoryController extends Controller
|
|||||||
* @param Carbon $start
|
* @param Carbon $start
|
||||||
* @param Carbon $end
|
* @param Carbon $end
|
||||||
*
|
*
|
||||||
* @return mixed|string
|
* @return string
|
||||||
* @throws JsonException
|
|
||||||
*/
|
*/
|
||||||
public function operations(Collection $accounts, Carbon $start, Carbon $end)
|
public function operations(Collection $accounts, Carbon $start, Carbon $end): string
|
||||||
{
|
{
|
||||||
// chart properties for cache:
|
// chart properties for cache:
|
||||||
$cache = new CacheProperties;
|
$cache = new CacheProperties;
|
||||||
@ -673,7 +672,7 @@ class CategoryController extends Controller
|
|||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = prefixView('reports.partials.categories', compact('report'))->render();
|
$result = (string)prefixView('reports.partials.categories', compact('report'))->render();
|
||||||
$cache->store($result);
|
$cache->store($result);
|
||||||
} catch (Throwable $e) { // @phpstan-ignore-line
|
} catch (Throwable $e) { // @phpstan-ignore-line
|
||||||
Log::error(sprintf('Could not render category::expenses: %s', $e->getMessage()));
|
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\Http\Controllers\RuleManagement;
|
||||||
use FireflyIII\Support\Search\SearchInterface;
|
use FireflyIII\Support\Search\SearchInterface;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Redirector;
|
use Illuminate\Routing\Redirector;
|
||||||
@ -237,15 +238,17 @@ class CreateController extends Controller
|
|||||||
/**
|
/**
|
||||||
* @param Rule $rule
|
* @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 new JsonResponse(['OK']);
|
||||||
|
|
||||||
return redirect(route('rules.index'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -28,6 +28,7 @@ use FireflyIII\Http\Requests\RuleGroupFormRequest;
|
|||||||
use FireflyIII\Models\RuleGroup;
|
use FireflyIII\Models\RuleGroup;
|
||||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||||
use Illuminate\Contracts\View\Factory;
|
use Illuminate\Contracts\View\Factory;
|
||||||
|
use Illuminate\Http\JsonResponse;
|
||||||
use Illuminate\Http\RedirectResponse;
|
use Illuminate\Http\RedirectResponse;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Routing\Redirector;
|
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();
|
$groupId = (int)$request->get('id');
|
||||||
$order = (int)$ruleGroup->order;
|
$ruleGroup= $this->repository->find($groupId);
|
||||||
if ($order < $maxOrder) {
|
if(null !== $ruleGroup) {
|
||||||
$newOrder = $order + 1;
|
$direction = $request->get('direction');
|
||||||
$this->repository->setOrder($ruleGroup, $newOrder);
|
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 new JsonResponse(['OK']);
|
||||||
return redirect(route('rules.index'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Edit a rule group.
|
* Edit a rule group.
|
||||||
*
|
*
|
||||||
@ -106,25 +121,6 @@ class EditController extends Controller
|
|||||||
return prefixView('rules.rule-group.edit', compact('ruleGroup', 'subTitle'));
|
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.
|
* Update the rule group.
|
||||||
*
|
*
|
||||||
|
@ -32,9 +32,6 @@ use Illuminate\Contracts\Config\Repository;
|
|||||||
*/
|
*/
|
||||||
class TrustProxies extends Middleware
|
class TrustProxies extends Middleware
|
||||||
{
|
{
|
||||||
/** @var int The headers to check. */
|
|
||||||
//protected $headers = Request::HEADER_X_FORWARDED_ALL;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TrustProxies constructor.
|
* TrustProxies constructor.
|
||||||
*
|
*
|
||||||
@ -42,11 +39,7 @@ class TrustProxies extends Middleware
|
|||||||
*/
|
*/
|
||||||
public function __construct(Repository $config)
|
public function __construct(Repository $config)
|
||||||
{
|
{
|
||||||
$trustedProxies = (string)config('firefly.trusted_proxies');
|
$this->proxies = (string)config('firefly.trusted_proxies');
|
||||||
$this->proxies = explode(',', $trustedProxies);
|
|
||||||
if ('**' === $trustedProxies) {
|
|
||||||
$this->proxies = '**';
|
|
||||||
}
|
|
||||||
parent::__construct($config);
|
parent::__construct($config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ class BillStoreRequest extends FormRequest
|
|||||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||||
'date' => 'required|date',
|
'date' => 'required|date',
|
||||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||||
'skip' => 'required|between:0,31',
|
'skip' => 'required|integer|gte:0|lte:31',
|
||||||
'active' => 'boolean',
|
'active' => 'boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -73,7 +73,7 @@ class BillUpdateRequest extends FormRequest
|
|||||||
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
'transaction_currency_id' => 'required|exists:transaction_currencies,id',
|
||||||
'date' => 'required|date',
|
'date' => 'required|date',
|
||||||
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly',
|
||||||
'skip' => 'required|between:0,31',
|
'skip' => 'required|integer|gte:0|lte:31',
|
||||||
'active' => 'boolean',
|
'active' => 'boolean',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ class BudgetFormStoreRequest extends FormRequest
|
|||||||
return [
|
return [
|
||||||
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
'name' => 'required|between:1,100|uniqueObjectForUser:budgets,name',
|
||||||
'active' => 'numeric|between:0,1',
|
'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_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_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',
|
'auto_budget_period' => 'in:daily,weekly,monthly,quarterly,half_year,yearly',
|
||||||
|
@ -73,7 +73,7 @@ class BudgetFormUpdateRequest extends FormRequest
|
|||||||
return [
|
return [
|
||||||
'name' => $nameRule,
|
'name' => $nameRule,
|
||||||
'active' => 'numeric|between:0,1',
|
'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_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_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',
|
'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',
|
'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title',
|
||||||
'first_date' => 'required|date|after:' . $today->format('Y-m-d'),
|
'first_date' => 'required|date|after:' . $today->format('Y-m-d'),
|
||||||
'repetition_type' => ['required', new ValidRecurrenceRepetitionValue, new ValidRecurrenceRepetitionType, 'between:1,20'],
|
'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:
|
// optional for recurrence:
|
||||||
'recurring_description' => 'between:0,65000',
|
'recurring_description' => 'between:0,65000',
|
||||||
|
@ -431,7 +431,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
|||||||
'user' => $recurrence->user_id,
|
'user' => $recurrence->user_id,
|
||||||
'currency_id' => (int)$transaction->transaction_currency_id,
|
'currency_id' => (int)$transaction->transaction_currency_id,
|
||||||
'currency_code' => null,
|
'currency_code' => null,
|
||||||
'description' => $recurrence->recurrenceTransactions()->first()->description,
|
'description' => $transactions->first()->description,
|
||||||
'amount' => $transaction->amount,
|
'amount' => $transaction->amount,
|
||||||
'budget_id' => $this->repository->getBudget($transaction),
|
'budget_id' => $this->repository->getBudget($transaction),
|
||||||
'budget_name' => null,
|
'budget_name' => null,
|
||||||
@ -452,7 +452,7 @@ class CreateRecurringTransactions implements ShouldQueue
|
|||||||
'tags' => $this->repository->getTags($transaction),
|
'tags' => $this->repository->getTags($transaction),
|
||||||
'piggy_bank_id' => $this->repository->getPiggyBank($transaction),
|
'piggy_bank_id' => $this->repository->getPiggyBank($transaction),
|
||||||
'piggy_bank_name' => null,
|
'piggy_bank_name' => null,
|
||||||
'bill_id' => null,
|
'bill_id' => $this->repository->getBillId($transaction),
|
||||||
'bill_name' => null,
|
'bill_name' => null,
|
||||||
'recurrence_total' => $total,
|
'recurrence_total' => $total,
|
||||||
'recurrence_count' => $count,
|
'recurrence_count' => $count,
|
||||||
|
@ -4,7 +4,8 @@ declare(strict_types=1);
|
|||||||
namespace FireflyIII\Ldap\Rules;
|
namespace FireflyIII\Ldap\Rules;
|
||||||
|
|
||||||
use LdapRecord\Laravel\Auth\Rule;
|
use LdapRecord\Laravel\Auth\Rule;
|
||||||
use LdapRecord\Models\ActiveDirectory\Group;
|
use LdapRecord\Models\Attributes\DistinguishedName;
|
||||||
|
use LdapRecord\Query\ObjectNotFoundException;
|
||||||
use Log;
|
use Log;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,22 +17,51 @@ class UserDefinedRule extends Rule
|
|||||||
* Check if the rule passes validation.
|
* Check if the rule passes validation.
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
|
* @throws ObjectNotFoundException
|
||||||
*/
|
*/
|
||||||
public function isValid()
|
public function isValid()
|
||||||
{
|
{
|
||||||
// LDAP_GROUP_FILTER
|
$extraFilter = config('ldap.extra_filter');
|
||||||
$groupFilter = config('ldap.group_filter');
|
Log::debug(sprintf('UserDefinedRule with extra filter "%s"', $extraFilter));
|
||||||
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)));
|
|
||||||
|
|
||||||
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;
|
namespace FireflyIII\Providers;
|
||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
|
use FireflyIII\Events\ActuallyLoggedIn;
|
||||||
use FireflyIII\Events\AdminRequestedTestMessage;
|
use FireflyIII\Events\AdminRequestedTestMessage;
|
||||||
use FireflyIII\Events\DestroyedTransactionGroup;
|
use FireflyIII\Events\DestroyedTransactionGroup;
|
||||||
use FireflyIII\Events\DetectedNewIPAddress;
|
use FireflyIII\Events\DetectedNewIPAddress;
|
||||||
@ -74,6 +75,8 @@ class EventServiceProvider extends ServiceProvider
|
|||||||
Login::class => [
|
Login::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
||||||
|
],
|
||||||
|
ActuallyLoggedIn::class => [
|
||||||
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
||||||
],
|
],
|
||||||
DetectedNewIPAddress::class => [
|
DetectedNewIPAddress::class => [
|
||||||
|
@ -251,7 +251,7 @@ class BillRepository implements BillRepositoryInterface
|
|||||||
$journalIds = $set->pluck('id')->toArray();
|
$journalIds = $set->pluck('id')->toArray();
|
||||||
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||||
$sum = bcadd($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');
|
$amount = (string)Transaction::whereIn('transaction_journal_id', $journalIds)->where('amount', '<', 0)->sum('amount');
|
||||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||||
$return[$currencyId] = bcadd($amount, $return[$currencyId]);
|
$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';
|
$sum = '0';
|
||||||
/** @var Bill $bill */
|
/** @var Bill $bill */
|
||||||
foreach ($bills as $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);
|
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||||
$total = $dates->count() - $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) {
|
if ($total > 0) {
|
||||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||||
$multi = bcmul($average, (string)$total);
|
$multi = bcmul($average, (string)$total);
|
||||||
$sum = bcadd($sum, $multi);
|
$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 = [];
|
$return = [];
|
||||||
/** @var Bill $bill */
|
/** @var Bill $bill */
|
||||||
foreach ($bills as $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);
|
$dates = $this->getPayDatesInRange($bill, $start, $end);
|
||||||
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
$count = $bill->transactionJournals()->after($start)->before($end)->count();
|
||||||
$total = $dates->count() - $count;
|
$total = $dates->count() - $count;
|
||||||
$currencyId = (int)$bill->transaction_currency_id;
|
$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) {
|
if ($total > 0) {
|
||||||
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
$average = bcdiv(bcadd($bill->amount_max, $bill->amount_min), '2');
|
||||||
$multi = bcmul($average, (string)$total);
|
$multi = bcmul($average, (string)$total);
|
||||||
$return[$currencyId] = $return[$currencyId] ?? '0';
|
$return[$currencyId] = $return[$currencyId] ?? '0';
|
||||||
$return[$currencyId] = bcadd($return[$currencyId], $multi);
|
$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')));
|
//Log::debug(sprintf('First currentstart is %s', $currentStart->format('Y-m-d')));
|
||||||
|
|
||||||
while ($currentStart <= $end) {
|
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);
|
$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')));
|
//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
|
if ($nextExpectedMatch > $end) {// If nextExpectedMatch is after end, we continue
|
||||||
|
@ -35,8 +35,7 @@ use Illuminate\Support\Collection;
|
|||||||
*/
|
*/
|
||||||
class OperationsRepository implements OperationsRepositoryInterface
|
class OperationsRepository implements OperationsRepositoryInterface
|
||||||
{
|
{
|
||||||
/** @var User */
|
private User $user;
|
||||||
private $user;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method returns a list of all the withdrawal transaction journals (as arrays) set in that period
|
* 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();
|
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;
|
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
|
* 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
|
* 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;
|
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;
|
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.
|
* Get the category from a recurring transaction transaction.
|
||||||
*
|
*
|
||||||
|
@ -329,10 +329,8 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
|||||||
*/
|
*/
|
||||||
public function resetOrder(): bool
|
public function resetOrder(): bool
|
||||||
{
|
{
|
||||||
$this->user->ruleGroups()->where('active', false)->update(['order' => 0]);
|
|
||||||
$set = $this->user
|
$set = $this->user
|
||||||
->ruleGroups()
|
->ruleGroups()
|
||||||
->where('active', true)
|
|
||||||
->whereNull('deleted_at')
|
->whereNull('deleted_at')
|
||||||
->orderBy('order', 'ASC')
|
->orderBy('order', 'ASC')
|
||||||
->orderBy('title', 'DESC')
|
->orderBy('title', 'DESC')
|
||||||
@ -363,7 +361,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
|||||||
{
|
{
|
||||||
$set = $ruleGroup->rules()
|
$set = $ruleGroup->rules()
|
||||||
->orderBy('order', 'ASC')
|
->orderBy('order', 'ASC')
|
||||||
->where('active', true)
|
|
||||||
->orderBy('title', 'DESC')
|
->orderBy('title', 'DESC')
|
||||||
->orderBy('updated_at', 'DESC')
|
->orderBy('updated_at', 'DESC')
|
||||||
->get(['rules.*']);
|
->get(['rules.*']);
|
||||||
|
@ -26,6 +26,7 @@ use Exception;
|
|||||||
use FireflyIII\Exceptions\FireflyException;
|
use FireflyIII\Exceptions\FireflyException;
|
||||||
use FireflyIII\Models\BudgetLimit;
|
use FireflyIII\Models\BudgetLimit;
|
||||||
use FireflyIII\Models\Role;
|
use FireflyIII\Models\Role;
|
||||||
|
use FireflyIII\Models\UserGroup;
|
||||||
use FireflyIII\User;
|
use FireflyIII\User;
|
||||||
use Illuminate\Database\QueryException;
|
use Illuminate\Database\QueryException;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
@ -164,7 +165,10 @@ class UserRepository implements UserRepositoryInterface
|
|||||||
public function destroy(User $user): bool
|
public function destroy(User $user): bool
|
||||||
{
|
{
|
||||||
Log::debug(sprintf('Calling delete() on user %d', $user->id));
|
Log::debug(sprintf('Calling delete() on user %d', $user->id));
|
||||||
|
|
||||||
|
$user->groupMemberships()->delete();
|
||||||
$user->delete();
|
$user->delete();
|
||||||
|
$this->deleteEmptyGroups();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -396,4 +400,20 @@ class UserRepository implements UserRepositoryInterface
|
|||||||
|
|
||||||
return true;
|
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 all(): Collection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function deleteEmptyGroups(): void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gives a user a role.
|
* Gives a user a role.
|
||||||
*
|
*
|
||||||
|
@ -23,6 +23,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace FireflyIII\Support\Http\Controllers;
|
namespace FireflyIII\Support\Http\Controllers;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trait BasicDataSupport
|
* Trait BasicDataSupport
|
||||||
*
|
*
|
||||||
@ -30,15 +32,28 @@ namespace FireflyIII\Support\Http\Controllers;
|
|||||||
trait BasicDataSupport
|
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 array $array
|
||||||
* @param int $entryId
|
* @param int $entryId
|
||||||
*
|
*
|
||||||
* @return null|mixed
|
* @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';
|
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' => [],
|
'budget_limits' => [],
|
||||||
];
|
];
|
||||||
|
|
||||||
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end);
|
$noBudget = $this->nbRepository->sumExpenses($this->start, $this->end, $this->accounts);
|
||||||
foreach ($noBudget as $noBudgetEntry) {
|
foreach ($noBudget as $noBudgetEntry) {
|
||||||
|
|
||||||
// currency information:
|
// currency information:
|
||||||
|
@ -66,6 +66,11 @@ class CategoryReportGenerator
|
|||||||
{
|
{
|
||||||
$earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts);
|
$earnedWith = $this->opsRepository->listIncome($this->start, $this->end, $this->accounts);
|
||||||
$spentWith = $this->opsRepository->listExpenses($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);
|
$earnedWithout = $this->noCatRepository->listIncome($this->start, $this->end, $this->accounts);
|
||||||
$spentWithout = $this->noCatRepository->listExpenses($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.
|
// needs four for-each loops.
|
||||||
foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout] as $data) {
|
foreach ([$earnedWith, $spentWith, $earnedWithout, $spentWithout, $transferredIn, $transferredOut] as $data) {
|
||||||
$this->processOpsArray($data);
|
$this->processOpsArray($data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,7 +156,7 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
$parser = new QueryParser();
|
$parser = new QueryParser();
|
||||||
try {
|
try {
|
||||||
$query1 = $parser->parse($query);
|
$query1 = $parser->parse($query);
|
||||||
} catch (TypeError|LogicException $e) {
|
} catch (TypeError | LogicException $e) {
|
||||||
Log::error($e->getMessage());
|
Log::error($e->getMessage());
|
||||||
Log::error(sprintf('Could not parse search: "%s".', $query));
|
Log::error(sprintf('Could not parse search: "%s".', $query));
|
||||||
throw new FireflyException('Invalid search value. See the logs.', 0, $e);
|
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
|
private function handleSearchNode(Node $searchNode): void
|
||||||
{
|
{
|
||||||
$class = get_class($searchNode);
|
$class = get_class($searchNode);
|
||||||
|
Log::debug(sprintf('Now in handleSearchNode(%s)', $class));
|
||||||
switch ($class) {
|
switch ($class) {
|
||||||
default:
|
default:
|
||||||
Log::error(sprintf('Cannot handle node %s', $class));
|
Log::error(sprintf('Cannot handle node %s', $class));
|
||||||
@ -252,7 +253,7 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
case Subquery::class:
|
case Subquery::class:
|
||||||
// loop all notes in subquery:
|
// loop all notes in subquery:
|
||||||
foreach ($searchNode->getNodes() as $subNode) { // @phpstan-ignore-line
|
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;
|
break;
|
||||||
case Word::class:
|
case Word::class:
|
||||||
@ -282,13 +283,15 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
'value' => (string)$value,
|
'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[] = [
|
$this->invalidOperators[] = [
|
||||||
'type' => $operator,
|
'type' => $operator,
|
||||||
'value' => (string)$value,
|
'value' => (string)$value,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -302,7 +305,7 @@ class OperatorQuerySearch implements SearchInterface
|
|||||||
*/
|
*/
|
||||||
private function updateCollector(string $operator, string $value): bool
|
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:
|
// check if alias, replace if necessary:
|
||||||
$operator = self::getRootOperator($operator);
|
$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
|
->get(['transactions.account_id', DB::raw('MAX(transaction_journals.date) AS max_date')]); // @phpstan-ignore-line
|
||||||
|
|
||||||
foreach ($set as $entry) {
|
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'));
|
$date->setTimezone(config('app.timezone'));
|
||||||
$list[(int)$entry->account_id] = $date;
|
$list[(int)$entry->account_id] = $date;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ class General extends AbstractExtension
|
|||||||
$this->mimeIcon(),
|
$this->mimeIcon(),
|
||||||
$this->markdown(),
|
$this->markdown(),
|
||||||
$this->floatval(),
|
$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.
|
* Used to convert 1024 to 1kb etc.
|
||||||
*
|
*
|
||||||
|
@ -60,9 +60,11 @@ class DeleteTransaction implements ActionInterface
|
|||||||
|
|
||||||
// trigger delete factory:
|
// trigger delete factory:
|
||||||
$journal = TransactionJournal::find($journal['transaction_group_id']);
|
$journal = TransactionJournal::find($journal['transaction_group_id']);
|
||||||
/** @var JournalDestroyService $service */
|
if (null !== $journal) {
|
||||||
$service = app(JournalDestroyService::class);
|
/** @var JournalDestroyService $service */
|
||||||
$service->destroy($journal);
|
$service = app(JournalDestroyService::class);
|
||||||
|
$service->destroy($journal);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -351,7 +351,7 @@ class TransactionGroupTransformer extends AbstractTransformer
|
|||||||
$bill = $this->getBill($journal->bill);
|
$bill = $this->getBill($journal->bill);
|
||||||
|
|
||||||
if (null !== $foreignAmount && null !== $foreignCurrency) {
|
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;
|
$longitude = null;
|
||||||
|
@ -67,7 +67,7 @@ trait TransferValidation
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// otherwise try to find the account:
|
// or try to find the account:
|
||||||
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
$search = $this->findExistingAccount($validTypes, (int)$accountId, (string)$accountName);
|
||||||
if (null === $search) {
|
if (null === $search) {
|
||||||
$this->destError = (string)trans('validation.transfer_dest_bad_data', ['id' => $accountId, 'name' => $accountName]);
|
$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
|
protected function validateTransactionQuery(Validator $validator): void
|
||||||
{
|
{
|
||||||
$data = $validator->getData();
|
$data = $validator->getData();
|
||||||
// assumption is all validation has already taken place
|
// assumption is all validation has already taken place and the query key exists.
|
||||||
// and the query key exists.
|
|
||||||
$json = json_decode($data['query'], true, 8, JSON_THROW_ON_ERROR);
|
$json = json_decode($data['query'], true, 8, JSON_THROW_ON_ERROR);
|
||||||
|
|
||||||
if (array_key_exists('account_id', $json['where'])
|
if (array_key_exists('account_id', $json['where'])
|
||||||
&& array_key_exists('account_id', $json['update'])
|
&& array_key_exists('account_id', $json['update'])
|
||||||
) {
|
) {
|
||||||
// find both accounts
|
// find both accounts, must be same type.
|
||||||
// must be same type.
|
|
||||||
// already validated: belongs to this user.
|
// already validated: belongs to this user.
|
||||||
$repository = app(AccountRepositoryInterface::class);
|
$repository = app(AccountRepositoryInterface::class);
|
||||||
$source = $repository->find((int)$json['where']['account_id']);
|
$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.
|
All notable changes to this project will be documented in this file.
|
||||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||||
|
|
||||||
## 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
|
### Added
|
||||||
- `/health` will return `200 OK` if Firefly III is up and running, thanks @ajgon!
|
- `/health` will return `200 OK` if Firefly III is up and running, thanks @ajgon!
|
||||||
|
@ -91,25 +91,25 @@
|
|||||||
"doctrine/dbal": "3.*",
|
"doctrine/dbal": "3.*",
|
||||||
"fideloper/proxy": "4.*",
|
"fideloper/proxy": "4.*",
|
||||||
"gdbots/query-parser": "^2.0",
|
"gdbots/query-parser": "^2.0",
|
||||||
"guzzlehttp/guzzle": "^7.2",
|
"guzzlehttp/guzzle": "^7.4",
|
||||||
"jc5/google2fa-laravel": "2.0.6",
|
"jc5/google2fa-laravel": "2.0.6",
|
||||||
"jc5/recovery": "^2",
|
"jc5/recovery": "^2",
|
||||||
"laravel/framework": "^8.51",
|
"laravel/framework": "^8.69",
|
||||||
"laravel/passport": "10.*",
|
"laravel/passport": "10.*",
|
||||||
"laravel/ui": "^3.0",
|
"laravel/ui": "^3.3",
|
||||||
"laravelcollective/html": "6.*",
|
"laravelcollective/html": "6.*",
|
||||||
"league/commonmark": "2.*",
|
"league/commonmark": "2.*",
|
||||||
"league/csv": "^9.6",
|
"league/csv": "^9.6",
|
||||||
"league/fractal": "0.*",
|
"league/fractal": "0.*",
|
||||||
"pragmarx/google2fa": "^8.0",
|
"pragmarx/google2fa": "^8.0",
|
||||||
"predis/predis": "^1.1",
|
"predis/predis": "^1.1",
|
||||||
|
"psr/log": "<3",
|
||||||
"ramsey/uuid": "^4.2",
|
"ramsey/uuid": "^4.2",
|
||||||
"rcrowe/twigbridge": "^0.12.1",
|
"rcrowe/twigbridge": "^0.12.1",
|
||||||
"spatie/data-transfer-object": "^3.1",
|
"spatie/data-transfer-object": "^3.1"
|
||||||
"psr/log": "<2"
|
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"barryvdh/laravel-debugbar": "^3.3",
|
"barryvdh/laravel-debugbar": "^3.6",
|
||||||
"barryvdh/laravel-ide-helper": "2.*",
|
"barryvdh/laravel-ide-helper": "2.*",
|
||||||
"filp/whoops": "2.*",
|
"filp/whoops": "2.*",
|
||||||
"fakerphp/faker": "1.*",
|
"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\AttributeHandler;
|
||||||
use FireflyIII\Ldap\Rules\UserDefinedRule;
|
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 [
|
return [
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
@ -108,8 +111,7 @@ return [
|
|||||||
|
|
||||||
'ldap' => [
|
'ldap' => [
|
||||||
'driver' => 'ldap',
|
'driver' => 'ldap',
|
||||||
//'model' => LdapRecord\Models\ActiveDirectory\User::class,
|
'model' => env('LDAP_DIALECT') === 'OpenLDAP' ? $openLDAP : $activeDirectory,
|
||||||
'model' => LdapRecord\Models\OpenLDAP\User::class,
|
|
||||||
'rules' => [
|
'rules' => [
|
||||||
UserDefinedRule::class,
|
UserDefinedRule::class,
|
||||||
],
|
],
|
||||||
|
@ -101,7 +101,7 @@ return [
|
|||||||
'webhooks' => true,
|
'webhooks' => true,
|
||||||
'handle_debts' => true,
|
'handle_debts' => true,
|
||||||
],
|
],
|
||||||
'version' => '5.6.2',
|
'version' => '5.6.3',
|
||||||
'api_version' => '1.5.4',
|
'api_version' => '1.5.4',
|
||||||
'db_version' => 18,
|
'db_version' => 18,
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ return [
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
'default' => env('LDAP_CONNECTION', 'default'),
|
'default' => env('LDAP_CONNECTION', 'default'),
|
||||||
|
'extra_filter' => env('LDAP_EXTRA_FILTER'),
|
||||||
'group_filter' => env('LDAP_GROUP_FILTER'),
|
'dialect' => env('LDAP_DIALECT'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
@ -15,15 +15,15 @@
|
|||||||
"laravel-mix": "^6",
|
"laravel-mix": "^6",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash.clonedeep": "^4.5.0",
|
"lodash.clonedeep": "^4.5.0",
|
||||||
"postcss": "^8.1.14",
|
"postcss": "^8.3.11",
|
||||||
"resolve-url-loader": "^4.0.0",
|
"resolve-url-loader": "^4.0.0",
|
||||||
"sass": "^1.39.2",
|
"sass": "^1.43.3",
|
||||||
"sass-loader": "^12.0.0",
|
"sass-loader": "^12.2.0",
|
||||||
"vue-i18n": "^8.24.2",
|
"vue-i18n": "^8.26.7",
|
||||||
"vue-loader": "^15",
|
"vue-loader": "^15",
|
||||||
"vue-template-compiler": "^2.6.12",
|
"vue-template-compiler": "^2.6.12",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
"webpack": "^5.52.1"
|
"webpack": "^5.62.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free": "^5.15.3",
|
"@fortawesome/fontawesome-free": "^5.15.3",
|
||||||
@ -32,7 +32,7 @@
|
|||||||
"axios-cache-adapter": "^2.7.3",
|
"axios-cache-adapter": "^2.7.3",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"bootstrap-vue": "^2.21.2",
|
"bootstrap-vue": "^2.21.2",
|
||||||
"chart.js": "^3.4.0",
|
"chart.js": "^3.6.0",
|
||||||
"icheck-bootstrap": "^3.0.1",
|
"icheck-bootstrap": "^3.0.1",
|
||||||
"jquery-ui": "^1.12.1",
|
"jquery-ui": "^1.12.1",
|
||||||
"leaflet": "^1.7.1",
|
"leaflet": "^1.7.1",
|
||||||
@ -40,7 +40,7 @@
|
|||||||
"localforage-memoryStorageDriver": "^0.9.2",
|
"localforage-memoryStorageDriver": "^0.9.2",
|
||||||
"overlayscrollbars": "^1.13.1",
|
"overlayscrollbars": "^1.13.1",
|
||||||
"sortablejs": "^1.14.0",
|
"sortablejs": "^1.14.0",
|
||||||
"uiv": "^1.3.1",
|
"uiv": "^1.4.1",
|
||||||
"v-calendar": "^2.3.2",
|
"v-calendar": "^2.3.2",
|
||||||
"vue-typeahead-bootstrap": "^2.8.0",
|
"vue-typeahead-bootstrap": "^2.8.0",
|
||||||
"vue2-leaflet": "^2.7.1"
|
"vue2-leaflet": "^2.7.1"
|
||||||
|
@ -97,7 +97,7 @@
|
|||||||
|
|
||||||
<GenericLocation :disabled="submitting" v-model="location" :title="$t('form.location')" :errors="errors.location"
|
<GenericLocation :disabled="submitting" v-model="location" :title="$t('form.location')" :errors="errors.location"
|
||||||
v-on:set-field="storeField($event)"/>
|
v-on:set-field="storeField($event)"/>
|
||||||
|
<!-- attachments -->
|
||||||
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
|
<GenericAttachments :disabled="submitting" :title="$t('form.attachments')" field-name="attachments" :errors="errors.attachments"
|
||||||
v-on:selected-attachments="selectedAttachments($event)"
|
v-on:selected-attachments="selectedAttachments($event)"
|
||||||
v-on:selected-no-attachments="selectedNoAttachments($event)"
|
v-on:selected-no-attachments="selectedNoAttachments($event)"
|
||||||
@ -275,6 +275,8 @@ export default {
|
|||||||
this.hasAttachments = false;
|
this.hasAttachments = false;
|
||||||
},
|
},
|
||||||
uploadedAttachments: function (e) {
|
uploadedAttachments: function (e) {
|
||||||
|
console.log('Response to event uploaded-attachments');
|
||||||
|
console.log(e);
|
||||||
this.finishSubmission();
|
this.finishSubmission();
|
||||||
},
|
},
|
||||||
submitForm: function (e) {
|
submitForm: function (e) {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
||||||
<!-- Custom Tabs -->
|
<!-- Custom Tabs -->
|
||||||
|
<!--
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header d-flex p-0">
|
<div class="card-header d-flex p-0">
|
||||||
<h3 class="card-title p-3">Tabs</h3>
|
<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="#budgets" data-toggle="tab">Budgets</a></li>
|
||||||
<li class="nav-item"><a class="nav-link" href="#categories" data-toggle="tab">Categories</a></li>
|
<li class="nav-item"><a class="nav-link" href="#categories" data-toggle="tab">Categories</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div><!-- /.card-header -->
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div class="tab-pane active" id="main_chart">
|
<div class="tab-pane active" id="main_chart">
|
||||||
1: main chart
|
1: main chart
|
||||||
</div>
|
</div>
|
||||||
<!-- /.tab-pane -->
|
|
||||||
<div class="tab-pane" id="budgets">
|
<div class="tab-pane" id="budgets">
|
||||||
2: tree map from/to budget
|
2: tree map from/to budget
|
||||||
</div>
|
</div>
|
||||||
<!-- /.tab-pane -->
|
|
||||||
<div class="tab-pane" id="categories">
|
<div class="tab-pane" id="categories">
|
||||||
2: tree map from/to cat
|
2: tree map from/to cat
|
||||||
</div>
|
</div>
|
||||||
<!-- /.tab-pane -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- /.tab-content -->
|
</div>
|
||||||
</div><!-- /.card-body -->
|
|
||||||
</div>
|
</div>
|
||||||
<!-- ./card -->
|
-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TransactionListLarge
|
<TransactionListLarge
|
||||||
:entries="rawTransactions"
|
:entries="rawTransactions"
|
||||||
:page="currentPage"
|
:page="currentPage"
|
||||||
|
ref="list"
|
||||||
:total="total"
|
:total="total"
|
||||||
:per-page="perPage"
|
:per-page="perPage"
|
||||||
:sort-desc="sortDesc"
|
:sort-desc="sortDesc"
|
||||||
v-on:jump-page="jumpToPage($event)"
|
v-on:jump-page="jumpToPage($event)"
|
||||||
|
v-on:refreshed-cache-key="refreshedKey"
|
||||||
/>
|
/>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
<div class="col-lg-12 col-md-6 col-sm-12 col-xs-12">
|
||||||
@ -106,7 +105,7 @@ export default {
|
|||||||
perPage: 51,
|
perPage: 51,
|
||||||
locale: 'en-US',
|
locale: 'en-US',
|
||||||
api: null,
|
api: null,
|
||||||
nameLoading:false
|
nameLoading: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
@ -132,16 +131,23 @@ export default {
|
|||||||
.then(response => {
|
.then(response => {
|
||||||
let start = new Intl.DateTimeFormat(this.locale, {year: 'numeric', month: 'long', day: 'numeric'}).format(this.start);
|
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);
|
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 () {
|
getTransactions: function () {
|
||||||
if (this.showReady && !this.loading) {
|
if (this.showReady && !this.loading) {
|
||||||
|
|
||||||
|
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
configureAxios().then(async (api) => {
|
configureAxios().then(async (api) => {
|
||||||
// console.log('Now getTransactions() x Start');
|
// console.log('Now getTransactions() x Start');
|
||||||
@ -149,8 +155,7 @@ export default {
|
|||||||
let endStr = format(this.end, 'y-MM-dd');
|
let endStr = format(this.end, 'y-MM-dd');
|
||||||
this.rawTransactions = [];
|
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)
|
api.get(url)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
@ -30,7 +30,6 @@
|
|||||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success" role="progressbar">
|
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-success" role="progressbar">
|
||||||
<span v-if="budgetLimit.pctGreen > 35">
|
<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)}) }}
|
{{ $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>
|
</span>
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +44,7 @@
|
|||||||
|
|
||||||
<div :aria-valuenow="budgetLimit.pctRed" :style="'width: '+ budgetLimit.pctRed + '%;'"
|
<div :aria-valuenow="budgetLimit.pctRed" :style="'width: '+ budgetLimit.pctRed + '%;'"
|
||||||
aria-valuemax="100" aria-valuemin="0" class="progress-bar bg-danger" role="progressbar">
|
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)}) }}
|
{{ $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>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
<td style="width:25%;">
|
<td style="width:25%;">
|
||||||
<a :href="'./budgets/show/' + budget.id">{{ budget.name }}</a>
|
<a :href="'./budgets/show/' + budget.id">{{ budget.name }}</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td> </td>
|
||||||
<td class="align-middle text-right">
|
<td class="align-middle text-right">
|
||||||
<span class="text-danger">
|
<span class="text-danger">
|
||||||
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budget.currency_code}).format(parseFloat(budget.spent)) }}
|
{{ Intl.NumberFormat(locale, {style: 'currency', currency: budget.currency_code}).format(parseFloat(budget.spent)) }}
|
||||||
|
@ -214,8 +214,8 @@ export default {
|
|||||||
let pctGreen = 0;
|
let pctGreen = 0;
|
||||||
let pctOrange = 0;
|
let pctOrange = 0;
|
||||||
let pctRed = 0;
|
let pctRed = 0;
|
||||||
//console.log('Collected "' + period + '" budget limit #' + currentId + ' (part of budget #' + budgetId + ')');
|
console.log('Collected "' + period + '" budget limit #' + currentId + ' (part of budget #' + budgetId + ')');
|
||||||
//console.log('Spent ' + spentFloat + ' of ' + amount);
|
console.log('Spent ' + spentFloatPos + ' of ' + amount);
|
||||||
|
|
||||||
// remove budget info from rawBudgets if it's there:
|
// remove budget info from rawBudgets if it's there:
|
||||||
this.filterBudgets(budgetId, currencyId);
|
this.filterBudgets(budgetId, currencyId);
|
||||||
@ -230,6 +230,12 @@ export default {
|
|||||||
pctOrange = (spentFloatPos / amount) * 100;
|
pctOrange = (spentFloatPos / amount) * 100;
|
||||||
pctRed = 100 - pctOrange;
|
pctRed = 100 - pctOrange;
|
||||||
}
|
}
|
||||||
|
// spent exactly on budget
|
||||||
|
if (0.0 !== spentFloatPos && spentFloatPos === amount) {
|
||||||
|
pctOrange = 0;
|
||||||
|
pctRed = 100;
|
||||||
|
}
|
||||||
|
|
||||||
let obj = {
|
let obj = {
|
||||||
id: currentId,
|
id: currentId,
|
||||||
amount: current.attributes.amount,
|
amount: current.attributes.amount,
|
||||||
|
@ -97,22 +97,22 @@ export default {
|
|||||||
// });
|
// });
|
||||||
|
|
||||||
// new code
|
// new code
|
||||||
// console.log('start of new');
|
console.log('start of new');
|
||||||
let files = this.$refs.att.files;
|
let files = this.$refs.att.files;
|
||||||
this.uploads = files.length;
|
this.uploads = files.length;
|
||||||
// loop all files and create attachments.
|
// loop all files and create attachments.
|
||||||
for (let i in files) {
|
for (let i in files) {
|
||||||
if (files.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
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:
|
// read file into file reader:
|
||||||
let current = files[i];
|
let current = files[i];
|
||||||
let fileReader = new FileReader();
|
let fileReader = new FileReader();
|
||||||
let theParent = this; // dont ask me why i need to do this.
|
let theParent = this; // dont ask me why i need to do this.
|
||||||
fileReader.onloadend = evt => {
|
fileReader.onloadend = evt => {
|
||||||
if (evt.target.readyState === FileReader.DONE) {
|
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 => {
|
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]));
|
return theParent.uploadAttachment(response.data.data.id, new Blob([evt.target.result]));
|
||||||
}).then(theParent.countAttachment);
|
}).then(theParent.countAttachment);
|
||||||
}
|
}
|
||||||
@ -121,7 +121,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (0 === files.length) {
|
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);
|
this.$emit('uploaded-attachments', this.transaction_journal_id);
|
||||||
}
|
}
|
||||||
// Promise.all(promises).then(response => {
|
// Promise.all(promises).then(response => {
|
||||||
@ -138,15 +138,15 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
countAttachment: function () {
|
countAttachment: function () {
|
||||||
this.uploaded++;
|
this.uploaded++;
|
||||||
// console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
|
console.log('Uploaded ' + this.uploaded + ' / ' + this.uploads);
|
||||||
if (this.uploaded >= this.uploads) {
|
if (this.uploaded >= this.uploads) {
|
||||||
// console.log('All files uploaded. Emit event for ' + this.transaction_journal_id + '(' + this.index + ')');
|
console.log('All files uploaded. Emit event for ' + this.uploadObjectId);
|
||||||
this.$emit('uploaded-attachments', this.transaction_journal_id);
|
this.$emit('uploaded-attachments', this.uploadObjectId);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
uploadAttachment: function (attachmentId, data) {
|
uploadAttachment: function (attachmentId, data) {
|
||||||
this.created++;
|
this.created++;
|
||||||
// console.log('Now in uploadAttachment()');
|
console.log('Now in uploadAttachment()');
|
||||||
const uploadUri = './api/v1/attachments/' + attachmentId + '/upload';
|
const uploadUri = './api/v1/attachments/' + attachmentId + '/upload';
|
||||||
return axios.post(uploadUri, data)
|
return axios.post(uploadUri, data)
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
:count="transactions.length"
|
:count="transactions.length"
|
||||||
:custom-fields="customFields"
|
:custom-fields="customFields"
|
||||||
:date="date"
|
:date="date"
|
||||||
|
ref="splitForms"
|
||||||
:destination-allowed-types="destinationAllowedTypes"
|
:destination-allowed-types="destinationAllowedTypes"
|
||||||
:index="index"
|
:index="index"
|
||||||
:source-allowed-types="sourceAllowedTypes"
|
:source-allowed-types="sourceAllowedTypes"
|
||||||
@ -70,7 +71,7 @@
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-outline-primary btn-block" @click="addTransactionArray"><span class="far fa-clone"></span> {{
|
<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>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@ -333,7 +334,6 @@ export default {
|
|||||||
for (let i in this.transactions) {
|
for (let i in this.transactions) {
|
||||||
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
if (this.transactions.hasOwnProperty(i) && /^0$|^[1-9]\d*$/.test(i) && i <= 4294967294) {
|
||||||
if (this.transactions.hasOwnProperty(i)) {
|
if (this.transactions.hasOwnProperty(i)) {
|
||||||
//this.
|
|
||||||
// console.log('Reset attachment #' + i);
|
// console.log('Reset attachment #' + i);
|
||||||
this.updateField({index: i, field: 'transaction_journal_id', value: 0});
|
this.updateField({index: i, field: 'transaction_journal_id', value: 0});
|
||||||
this.updateField({index: i, field: 'errors', value: this.defaultErrors})
|
this.updateField({index: i, field: 'errors', value: this.defaultErrors})
|
||||||
@ -344,7 +344,8 @@ export default {
|
|||||||
// reset the form:
|
// reset the form:
|
||||||
if (this.resetFormAfter) {
|
if (this.resetFormAfter) {
|
||||||
this.resetTransactions();
|
this.resetTransactions();
|
||||||
this.addTransaction();
|
setTimeout(this.addTransaction, 50);
|
||||||
|
|
||||||
}
|
}
|
||||||
return Promise.resolve({response: 'from finaliseSubmission'});
|
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.updateField({index: payload.index, field: payload.direction + '_account_currency_symbol', value: payload.currency_symbol});
|
||||||
|
|
||||||
//this.calculateTransactionType(payload.index);
|
//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) {
|
storeField: function (payload) {
|
||||||
this.updateField(payload);
|
this.updateField(payload);
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
|
<!--
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -36,6 +37,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
<!-- page is ignored for the time being -->
|
<!-- page is ignored for the time being -->
|
||||||
<TransactionListLarge
|
<TransactionListLarge
|
||||||
:entries="rawTransactions"
|
:entries="rawTransactions"
|
||||||
@ -44,7 +46,9 @@
|
|||||||
:per-page="perPage"
|
:per-page="perPage"
|
||||||
:sort-desc="sortDesc"
|
:sort-desc="sortDesc"
|
||||||
v-on:jump-page="jumpToPage($event)"
|
v-on:jump-page="jumpToPage($event)"
|
||||||
|
v-on:refreshed-cache-key="refreshedKey"
|
||||||
/>
|
/>
|
||||||
|
<!--
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-xl-2 col-lg-4 col-sm-6 col-xs-12" v-for="range in ranges">
|
<div class="col-xl-2 col-lg-4 col-sm-6 col-xs-12" v-for="range in ranges">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
@ -56,7 +60,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
-->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -141,13 +147,12 @@ export default {
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('root', ['refreshCacheKey',]),
|
...mapMutations('root', ['refreshCacheKey',]),
|
||||||
newCacheKey: function () {
|
refreshedKey: function () {
|
||||||
this.refreshCacheKey();
|
|
||||||
this.downloaded = false;
|
this.downloaded = false;
|
||||||
this.accounts = [];
|
this.rawTransactions = [];
|
||||||
this.getTransactionList();
|
this.getTransactionList();
|
||||||
},
|
},
|
||||||
jumpToPage: function(event) {
|
jumpToPage: function (event) {
|
||||||
// console.log('noticed a change!');
|
// console.log('noticed a change!');
|
||||||
this.currentPage = event.page;
|
this.currentPage = event.page;
|
||||||
this.downloadTransactionList(event.page);
|
this.downloadTransactionList(event.page);
|
||||||
|
@ -58,6 +58,8 @@
|
|||||||
:source-allowed-types="sourceAllowedTypes"
|
:source-allowed-types="sourceAllowedTypes"
|
||||||
:transaction-type="transactionType"
|
:transaction-type="transactionType"
|
||||||
direction="source"
|
direction="source"
|
||||||
|
ref="sourceAccount"
|
||||||
|
v-on:selected-account="triggerNextAccount($event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<!-- switcharoo! -->
|
<!-- switcharoo! -->
|
||||||
@ -79,6 +81,7 @@
|
|||||||
:destination-allowed-types="destinationAllowedTypes"
|
:destination-allowed-types="destinationAllowedTypes"
|
||||||
:errors="transaction.errors.destination"
|
:errors="transaction.errors.destination"
|
||||||
:index="index"
|
:index="index"
|
||||||
|
ref="destinationAccount"
|
||||||
:transaction-type="transactionType"
|
:transaction-type="transactionType"
|
||||||
:source-allowed-types="sourceAllowedTypes"
|
:source-allowed-types="sourceAllowedTypes"
|
||||||
direction="destination"
|
direction="destination"
|
||||||
@ -93,6 +96,7 @@
|
|||||||
<!-- AMOUNT -->
|
<!-- AMOUNT -->
|
||||||
<TransactionAmount
|
<TransactionAmount
|
||||||
v-on="$listeners"
|
v-on="$listeners"
|
||||||
|
ref="amount"
|
||||||
:amount="transaction.amount"
|
:amount="transaction.amount"
|
||||||
:destination-currency-symbol="this.transaction.destination_account_currency_symbol"
|
:destination-currency-symbol="this.transaction.destination_account_currency_symbol"
|
||||||
:errors="transaction.errors.amount"
|
:errors="transaction.errors.amount"
|
||||||
@ -379,6 +383,13 @@ export default {
|
|||||||
// console.log('Will remove transaction ' + this.index);
|
// console.log('Will remove transaction ' + this.index);
|
||||||
this.$emit('remove-transaction', {index: 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: {
|
computed: {
|
||||||
splitDate: function () {
|
splitDate: function () {
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
:placeholder="$t('firefly.' + direction + '_account')"
|
:placeholder="$t('firefly.' + direction + '_account')"
|
||||||
:serializer="item => item.name_with_balance"
|
:serializer="item => item.name_with_balance"
|
||||||
:showOnFocus=true
|
:showOnFocus=true
|
||||||
|
ref="inputThing"
|
||||||
aria-autocomplete="none"
|
aria-autocomplete="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@hit="userSelectedAccount"
|
@hit="userSelectedAccount"
|
||||||
@ -119,6 +120,12 @@ export default {
|
|||||||
getACURL: function (types, query) {
|
getACURL: function (types, query) {
|
||||||
return './api/v1/autocomplete/accounts?types=' + types.join(',') + '&query=' + 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) {
|
userSelectedAccount: function (event) {
|
||||||
// console.log('userSelectedAccount!');
|
// console.log('userSelectedAccount!');
|
||||||
// console.log('To prevent invalid propogation, set selectedAccountTrigger = true');
|
// console.log('To prevent invalid propogation, set selectedAccountTrigger = true');
|
||||||
@ -205,10 +212,14 @@ export default {
|
|||||||
currency_id: value.currency_id,
|
currency_id: value.currency_id,
|
||||||
currency_code: value.currency_code,
|
currency_code: value.currency_code,
|
||||||
currency_symbol: value.currency_symbol,
|
currency_symbol: value.currency_symbol,
|
||||||
|
user_selected: true,
|
||||||
}
|
}
|
||||||
|
// jump to next field somehow.
|
||||||
|
|
||||||
);
|
);
|
||||||
//console.log('watch::selectedAccount() will now set accountName because selectedAccountTrigger = true');
|
//console.log('watch::selectedAccount() will now set accountName because selectedAccountTrigger = true');
|
||||||
this.accountName = value.name;
|
this.accountName = value.name;
|
||||||
|
|
||||||
}
|
}
|
||||||
if (false === this.selectedAccountTrigger) {
|
if (false === this.selectedAccountTrigger) {
|
||||||
//console.log('watch::selectedAccount() will NOT set accountName because selectedAccountTrigger = false');
|
//console.log('watch::selectedAccount() will NOT set accountName because selectedAccountTrigger = false');
|
||||||
@ -238,6 +249,7 @@ export default {
|
|||||||
currency_id: null,
|
currency_id: null,
|
||||||
currency_code: null,
|
currency_code: null,
|
||||||
currency_symbol: null,
|
currency_symbol: null,
|
||||||
|
user_selected: false
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// this.account = {name: value, type: null, id: null, currency_id: null, currency_code: null, currency_symbol: null};
|
// this.account = {name: value, type: null, id: null, currency_id: null, currency_code: null, currency_symbol: null};
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
name="amount[]"
|
name="amount[]"
|
||||||
type="number"
|
type="number"
|
||||||
|
ref="input"
|
||||||
step="any"
|
step="any"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
@ -71,7 +72,10 @@ export default {
|
|||||||
methods: {
|
methods: {
|
||||||
formatNumber(str) {
|
formatNumber(str) {
|
||||||
return parseFloat(str).toFixed(this.fractionDigits);
|
return parseFloat(str).toFixed(this.fractionDigits);
|
||||||
}
|
},
|
||||||
|
giveFocus: function() {
|
||||||
|
this.$refs.input.focus();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -53,9 +53,9 @@
|
|||||||
</template>
|
</template>
|
||||||
<template #cell(type)="data">
|
<template #cell(type)="data">
|
||||||
<span v-if="!data.item.dummy">
|
<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-right" v-if="'deposit' === data.item.type.toLowerCase()"></span>
|
||||||
<span class="fas fa-long-arrow-alt-left" v-else-if="'withdrawal' === data.item.type"></span>
|
<span class="fas fa-long-arrow-alt-left" v-if="'withdrawal' === data.item.type.toLowerCase()"></span>
|
||||||
<span class="fas fa-long-arrows-alt-h" v-else-if="'transfer' === data.item.type"></span>
|
<span class="fas fa-arrows-alt-h" v-if="'transfer' === data.item.type.toLowerCase()"></span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
<template #cell(description)="data">
|
<template #cell(description)="data">
|
||||||
@ -86,7 +86,7 @@
|
|||||||
<span :class="'text-danger ' + (!data.item.collapsed ? 'font-weight-bold' : '')" v-if="'withdrawal' === data.item.type">
|
<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) }}
|
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(-data.item.amount) }}
|
||||||
</span>
|
</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) }}
|
{{ Intl.NumberFormat(locale, {style: 'currency', currency: data.item.currency_code}).format(data.item.amount) }}
|
||||||
</span>
|
</span>
|
||||||
<br />
|
<br />
|
||||||
@ -220,21 +220,26 @@ export default {
|
|||||||
this.$emit('jump-page', {page: value});
|
this.$emit('jump-page', {page: value});
|
||||||
},
|
},
|
||||||
entries: function (value) {
|
entries: function (value) {
|
||||||
|
console.log('detected new transactions!');
|
||||||
this.parseTransactions();
|
this.parseTransactions();
|
||||||
},
|
},
|
||||||
|
value: function(value) {
|
||||||
|
console.log('Watch value!');
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
...mapMutations('root', ['refreshCacheKey',]),
|
...mapMutations('root', ['refreshCacheKey',]),
|
||||||
parseTransactions: function () {
|
parseTransactions: function () {
|
||||||
|
this.transactions = [];
|
||||||
// console.log('Start of parseTransactions. Count of entries is ' + this.entries.length + ' and page is ' + this.page);
|
// console.log('Start of parseTransactions. Count of entries is ' + this.entries.length + ' and page is ' + this.page);
|
||||||
// console.log('Reported total is ' + this.total);
|
// console.log('Reported total is ' + this.total);
|
||||||
if (0 === this.entries.length) {
|
if (0 === this.entries.length) {
|
||||||
// console.log('Will not render now');
|
console.log('Will not render now');
|
||||||
return;
|
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++) {
|
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('Push dummy to index ' + i);
|
||||||
// console.log('Now have ' + this.transactions.length + ' transactions');
|
// console.log('Now have ' + this.transactions.length + ' transactions');
|
||||||
}
|
}
|
||||||
@ -259,8 +264,9 @@ export default {
|
|||||||
this.loading = false;
|
this.loading = false;
|
||||||
},
|
},
|
||||||
newCacheKey: function () {
|
newCacheKey: function () {
|
||||||
alert('TODO');
|
|
||||||
this.refreshCacheKey();
|
this.refreshCacheKey();
|
||||||
|
console.log('Cache key is now ' + this.cacheKey);
|
||||||
|
this.$emit('refreshed-cache-key');
|
||||||
},
|
},
|
||||||
updateFieldList: function () {
|
updateFieldList: function () {
|
||||||
this.fields = [
|
this.fields = [
|
||||||
@ -314,6 +320,7 @@ export default {
|
|||||||
let info = transaction.attributes.transactions[i];
|
let info = transaction.attributes.transactions[i];
|
||||||
let split = {};
|
let split = {};
|
||||||
row.amount = row.amount + parseFloat(info.amount);
|
row.amount = row.amount + parseFloat(info.amount);
|
||||||
|
split.type = info.type;
|
||||||
split.description = info.description;
|
split.description = info.description;
|
||||||
split.amount = info.amount;
|
split.amount = info.amount;
|
||||||
split.currency_code = info.currency_code;
|
split.currency_code = info.currency_code;
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
"daily_budgets": "Denn\u00ed rozpo\u010dty",
|
"daily_budgets": "Denn\u00ed rozpo\u010dty",
|
||||||
"weekly_budgets": "T\u00fddenn\u00ed rozpo\u010dty",
|
"weekly_budgets": "T\u00fddenn\u00ed rozpo\u010dty",
|
||||||
"monthly_budgets": "M\u011bs\u00ed\u010dn\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",
|
"quarterly_budgets": "\u010ctvrtletn\u00ed rozpo\u010dty",
|
||||||
"create_new_expense": "Vytvo\u0159it v\u00fddajov\u00fd \u00fa\u010det",
|
"create_new_expense": "Vytvo\u0159it v\u00fddajov\u00fd \u00fa\u010det",
|
||||||
"create_new_revenue": "Vytvo\u0159it nov\u00fd p\u0159\u00edjmov\u00fd \u00fa\u010det",
|
"create_new_revenue": "Vytvo\u0159it nov\u00fd p\u0159\u00edjmov\u00fd \u00fa\u010det",
|
||||||
|
@ -70,7 +70,7 @@
|
|||||||
"daily_budgets": "Presupuestos diarios",
|
"daily_budgets": "Presupuestos diarios",
|
||||||
"weekly_budgets": "Presupuestos semanales",
|
"weekly_budgets": "Presupuestos semanales",
|
||||||
"monthly_budgets": "Presupuestos mensuales",
|
"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",
|
"quarterly_budgets": "Presupuestos trimestrales",
|
||||||
"create_new_expense": "Crear nueva cuenta de gastos",
|
"create_new_expense": "Crear nueva cuenta de gastos",
|
||||||
"create_new_revenue": "Crear nueva cuenta de ingresos",
|
"create_new_revenue": "Crear nueva cuenta de ingresos",
|
||||||
@ -142,27 +142,27 @@
|
|||||||
"transaction_expand_split": "Expandir divisi\u00f3n",
|
"transaction_expand_split": "Expandir divisi\u00f3n",
|
||||||
"transaction_collapse_split": "Colapsar divisi\u00f3n",
|
"transaction_collapse_split": "Colapsar divisi\u00f3n",
|
||||||
"default_group_title_name": "(sin agrupaci\u00f3n)",
|
"default_group_title_name": "(sin agrupaci\u00f3n)",
|
||||||
"bill_repeats_weekly": "Repeats weekly",
|
"bill_repeats_weekly": "Repetir semanalmente",
|
||||||
"bill_repeats_monthly": "Repeats monthly",
|
"bill_repeats_monthly": "Repetir mensualmente",
|
||||||
"bill_repeats_quarterly": "Repeats quarterly",
|
"bill_repeats_quarterly": "Repite trimestralmente",
|
||||||
"bill_repeats_half-year": "Repeats every half year",
|
"bill_repeats_half-year": "Repetir cada 6 meses",
|
||||||
"bill_repeats_yearly": "Repeats yearly",
|
"bill_repeats_yearly": "Repetir anualmente",
|
||||||
"bill_repeats_weekly_other": "Repeats every other week",
|
"bill_repeats_weekly_other": "Repetir cada dos semanas",
|
||||||
"bill_repeats_monthly_other": "Repeats every other month",
|
"bill_repeats_monthly_other": "Repetir cada dos meses",
|
||||||
"bill_repeats_quarterly_other": "Repeats every other quarter",
|
"bill_repeats_quarterly_other": "Repetir cada dos trimestres",
|
||||||
"bill_repeats_half-year_other": "Repeats yearly",
|
"bill_repeats_half-year_other": "Repetir anualmente",
|
||||||
"bill_repeats_yearly_other": "Repeats every other year",
|
"bill_repeats_yearly_other": "Repetir cada dos a\u00f1os",
|
||||||
"bill_repeats_weekly_skip": "Repeats every {skip} weeks",
|
"bill_repeats_weekly_skip": "Repetir cada {skip} semanas",
|
||||||
"bill_repeats_monthly_skip": "Repeats every {skip} months",
|
"bill_repeats_monthly_skip": "Repetir cada {skip} meses",
|
||||||
"bill_repeats_quarterly_skip": "Repeats every {skip} quarters",
|
"bill_repeats_quarterly_skip": "Repetir cada {skip} trimestres",
|
||||||
"bill_repeats_half-year_skip": "Repeats every {skip} half years",
|
"bill_repeats_half-year_skip": "Repetir cada {skip} medios a\u00f1os",
|
||||||
"bill_repeats_yearly_skip": "Repeats every {skip} years",
|
"bill_repeats_yearly_skip": "Repetir cada {skip} a\u00f1os",
|
||||||
"not_expected_period": "No se espera en este per\u00edodo",
|
"not_expected_period": "No se espera en este per\u00edodo",
|
||||||
"subscriptions": "Subscriptions",
|
"subscriptions": "Suscripciones",
|
||||||
"bill_expected_date_js": "Expected {date}",
|
"bill_expected_date_js": "Expected {date}",
|
||||||
"inactive": "Inactivo",
|
"inactive": "Inactivo",
|
||||||
"forever": "Forever",
|
"forever": "Siempre",
|
||||||
"extension_date_is": "Extension date is {date}",
|
"extension_date_is": "Fecha de extensi\u00f3n es {date}",
|
||||||
"create_new_bill": "Crear nueva factura",
|
"create_new_bill": "Crear nueva factura",
|
||||||
"store_new_bill": "Crear factura",
|
"store_new_bill": "Crear factura",
|
||||||
"repeat_freq_yearly": "anualmente",
|
"repeat_freq_yearly": "anualmente",
|
||||||
@ -170,13 +170,13 @@
|
|||||||
"repeat_freq_quarterly": "trimestralmente",
|
"repeat_freq_quarterly": "trimestralmente",
|
||||||
"repeat_freq_monthly": "mensualmente",
|
"repeat_freq_monthly": "mensualmente",
|
||||||
"repeat_freq_weekly": "semanalmente",
|
"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_liabilities_account": "Actualizar pasivo",
|
||||||
"update_expense_account": "Actualizar cuenta de gastos",
|
"update_expense_account": "Actualizar cuenta de gastos",
|
||||||
"update_revenue_account": "Actualizar cuenta de ingresos",
|
"update_revenue_account": "Actualizar cuenta de ingresos",
|
||||||
"update_undefined_account": "Update account",
|
"update_undefined_account": "Actualizar cuenta",
|
||||||
"update_asset_account": "Actualizar cuenta de activos",
|
"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": {
|
"list": {
|
||||||
"piggy_bank": "Alcancilla",
|
"piggy_bank": "Alcancilla",
|
||||||
@ -197,10 +197,10 @@
|
|||||||
"liability_direction": "Pasivo entrada\/salida",
|
"liability_direction": "Pasivo entrada\/salida",
|
||||||
"currentBalance": "Balance actual",
|
"currentBalance": "Balance actual",
|
||||||
"next_expected_match": "Pr\u00f3xima coincidencia esperada",
|
"next_expected_match": "Pr\u00f3xima coincidencia esperada",
|
||||||
"expected_info": "Next expected transaction",
|
"expected_info": "Siguiente transacci\u00f3n esperada",
|
||||||
"start_date": "Start date",
|
"start_date": "Fecha de inicio",
|
||||||
"end_date": "End date",
|
"end_date": "Fecha fin",
|
||||||
"payment_info": "Payment information"
|
"payment_info": "Informaci\u00f3n del pago"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"html_language": "es",
|
"html_language": "es",
|
||||||
@ -221,7 +221,7 @@
|
|||||||
"repeat_freq": "Repetici\u00f3n",
|
"repeat_freq": "Repetici\u00f3n",
|
||||||
"skip": "Saltar",
|
"skip": "Saltar",
|
||||||
"startdate": "Fecha de inicio",
|
"startdate": "Fecha de inicio",
|
||||||
"enddate": "End date",
|
"enddate": "Fecha fin",
|
||||||
"object_group": "Grupo",
|
"object_group": "Grupo",
|
||||||
"attachments": "Adjuntos",
|
"attachments": "Adjuntos",
|
||||||
"active": "Activo",
|
"active": "Activo",
|
||||||
@ -252,6 +252,6 @@
|
|||||||
"amount_max": "Importe m\u00e1ximo",
|
"amount_max": "Importe m\u00e1ximo",
|
||||||
"start_date": "Inicio del rango",
|
"start_date": "Inicio del rango",
|
||||||
"end_date": "Final 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",
|
"daily_budgets": "Dagliga budgetar",
|
||||||
"weekly_budgets": "Veckovis budgetar",
|
"weekly_budgets": "Veckovis budgetar",
|
||||||
"monthly_budgets": "M\u00e5natliga 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",
|
"quarterly_budgets": "Kvartalsbudgetar",
|
||||||
"create_new_expense": "Skapa ett nytt utgiftskonto",
|
"create_new_expense": "Skapa ett nytt utgiftskonto",
|
||||||
"create_new_revenue": "Skapa ett nytt int\u00e4ktskonto",
|
"create_new_revenue": "Skapa ett nytt int\u00e4ktskonto",
|
||||||
@ -174,9 +174,9 @@
|
|||||||
"update_liabilities_account": "Uppdatera skuld",
|
"update_liabilities_account": "Uppdatera skuld",
|
||||||
"update_expense_account": "Uppdatera utgiftskonto",
|
"update_expense_account": "Uppdatera utgiftskonto",
|
||||||
"update_revenue_account": "Uppdatera int\u00e4ktskonto",
|
"update_revenue_account": "Uppdatera int\u00e4ktskonto",
|
||||||
"update_undefined_account": "Update account",
|
"update_undefined_account": "Uppdatera konto",
|
||||||
"update_asset_account": "Uppdatera tillg\u00e5ngskonto",
|
"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": {
|
"list": {
|
||||||
"piggy_bank": "Spargris",
|
"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": {
|
"devDependencies": {
|
||||||
"@johmun/vue-tags-input": "^2",
|
"@johmun/vue-tags-input": "^2",
|
||||||
"@vue/compiler-sfc": "^3.2.19",
|
"@vue/compiler-sfc": "^3.2.21",
|
||||||
"axios": "^0.22",
|
"axios": "^0.24",
|
||||||
"bootstrap-sass": "^3",
|
"bootstrap-sass": "^3",
|
||||||
"cross-env": "^7.0",
|
"cross-env": "^7.0",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"jquery": "^3",
|
"jquery": "^3",
|
||||||
"laravel-mix": "^6.0",
|
"laravel-mix": "^6.0",
|
||||||
"postcss": "^8.3",
|
"postcss": "^8.3",
|
||||||
"uiv": "^1.3",
|
"uiv": "^1.4",
|
||||||
"vue": "^2.6",
|
"vue": "^2.6",
|
||||||
"vue-i18n": "^8.25",
|
"vue-i18n": "^8.26",
|
||||||
"vue-loader": "^15",
|
"vue-loader": "^15",
|
||||||
"vue-template-compiler": "^2.6"
|
"vue-template-compiler": "^2.6"
|
||||||
}
|
}
|
||||||
|
2
public/v1/js/app_vue.js
vendored
2
public/v1/js/app_vue.js
vendored
File diff suppressed because one or more lines are too long
2
public/v1/js/create_transaction.js
vendored
2
public/v1/js/create_transaction.js
vendored
File diff suppressed because one or more lines are too long
2
public/v1/js/edit_transaction.js
vendored
2
public/v1/js/edit_transaction.js
vendored
File diff suppressed because one or more lines are too long
29
public/v1/js/ff/rules/index.js
vendored
29
public/v1/js/ff/rules/index.js
vendored
@ -59,6 +59,32 @@ function readCookie(name) {
|
|||||||
return null;
|
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 () {
|
$(function () {
|
||||||
"use strict";
|
"use strict";
|
||||||
@ -71,6 +97,9 @@ $(function () {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$('.move-group').click(moveRuleGroup);
|
||||||
|
$('.duplicate-rule').click(duplicateRule);
|
||||||
|
|
||||||
$('.rules-box').each(function (i, v) {
|
$('.rules-box').each(function (i, v) {
|
||||||
var box = $(v);
|
var box = $(v);
|
||||||
var groupId = box.data('group');
|
var groupId = box.data('group');
|
||||||
|
2
public/v1/js/profile.js
vendored
2
public/v1/js/profile.js
vendored
File diff suppressed because one or more lines are too long
4
public/v2/css/app.css
vendored
4
public/v2/css/app.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/accounts/create.js
vendored
2
public/v2/js/accounts/create.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/accounts/delete.js
vendored
2
public/v2/js/accounts/delete.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/accounts/edit.js
vendored
2
public/v2/js/accounts/edit.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/accounts/index.js
vendored
2
public/v2/js/accounts/index.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/accounts/show.js
vendored
2
public/v2/js/accounts/show.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/bills/create.js
vendored
2
public/v2/js/bills/create.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/bills/index.js
vendored
2
public/v2/js/bills/index.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/budgets/index.js
vendored
2
public/v2/js/budgets/index.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/dashboard.js
vendored
2
public/v2/js/dashboard.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/transactions/create.js
vendored
2
public/v2/js/transactions/create.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/transactions/edit.js
vendored
2
public/v2/js/transactions/edit.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
public/v2/js/transactions/index.js
vendored
2
public/v2/js/transactions/index.js
vendored
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