Compare commits

..

136 Commits
5.2.2 ... 5.2.6

Author SHA1 Message Date
James Cole
09990acaa2 Merge branch 'release/5.2.6' 2020-05-22 17:54:02 +02:00
James Cole
a73247ec8c Some last minute translations. 2020-05-22 17:53:09 +02:00
James Cole
e98d43dd65 New meta files for 5.2.6 2020-05-22 14:15:57 +02:00
James Cole
dbd68cedc9 Code for #3309 2020-05-22 13:52:33 +02:00
James Cole
e6e8200912 Merge pull request #3381 from bpatath/feature/add-single-sign-on
Feature/add single sign on
2020-05-22 04:27:39 +00:00
bpatath
7b1380366b Register SSO middleware if enabled in configuration 2020-05-21 21:05:03 +02:00
bpatath
1eda806c17 Fix outdated AdLdap2 configuration files 2020-05-21 21:04:17 +02:00
James Cole
782ecca6a9 Warning if people disabled update checking. Every 4 weeks. 2020-05-20 06:40:18 +02:00
James Cole
e6338705a7 Fix #3376 2020-05-19 19:37:12 +02:00
James Cole
09226e6f12 New translations. 2020-05-19 13:29:47 +02:00
James Cole
5ff1991cdc Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2020-05-19 11:19:13 +02:00
James Cole
4ebb6de520 Reset rule order on index. #3376 2020-05-19 11:18:58 +02:00
James Cole
1ba7d65582 Warning if demo user. 2020-05-18 21:21:34 +02:00
James Cole
4e6063a4f8 Demo user can't set locale 2020-05-18 21:19:45 +02:00
James Cole
85476f3549 Fix some strings. 2020-05-18 21:17:59 +02:00
James Cole
ef9714b7e0 Fix #3374 2020-05-18 14:50:07 +02:00
James Cole
8d20029557 Fix #3368 2020-05-17 19:17:19 +02:00
James Cole
0f04b44ca1 Fix # 2020-05-17 06:49:26 +02:00
James Cole
44ed45502e Change in error handling. 2020-05-16 16:57:48 +02:00
James Cole
5bcafe1311 Change in error handling. 2020-05-16 16:57:26 +02:00
James Cole
5a771ccc5f Run yarn again #3363 2020-05-16 16:56:08 +02:00
James Cole
c6145b4a3b Auto generated JSON files for new strings in other languages. #3363 2020-05-16 16:35:23 +02:00
James Cole
b20aeca849 Auto-generated translation files from crowdin #3363 2020-05-16 16:33:57 +02:00
James Cole
793918f2f3 Add new strings to global translation files. #3363 2020-05-16 16:30:17 +02:00
James Cole
5d410e577e Merge pull request #3365 from sephrat/master
Fix #3363
2020-05-16 14:27:39 +00:00
James Cole
9ec786ec3a Merge branch 'develop' into master 2020-05-16 14:27:31 +00:00
Florian Dupret
e532b4d4fc Fix #3363 2020-05-16 15:01:19 +02:00
James Cole
58585d03c6 Fix rounding thing. 2020-05-16 13:09:37 +02:00
James Cole
a4f66b3d86 Date before and after triggers 2020-05-16 12:55:54 +02:00
James Cole
c847621874 First "date is" trigger for #3049 2020-05-16 12:11:06 +02:00
James Cole
86f14885eb Added a rule action that will delete the transactions it matches. 2020-05-16 11:21:26 +02:00
James Cole
173e196bc8 Some experimental fixes for #3011 2020-05-16 10:45:40 +02:00
James Cole
7505db871f Update packages. 2020-05-16 06:59:51 +02:00
James Cole
946dde8957 Add ability to store recurring telemetry. Not enabled. 2020-05-16 06:59:41 +02:00
James Cole
9a52cfbfbe Add some debug info 2020-05-16 06:59:15 +02:00
James Cole
b248bd6d0c Remove code. 2020-05-15 19:59:23 +02:00
James Cole
3fff9ad0a2 Updated translations. 2020-05-15 19:31:19 +02:00
James Cole
a695a1bba2 Fix #3355 2020-05-12 20:40:28 +02:00
James Cole
91e384aae8 Fix #3350 2020-05-10 20:08:16 +02:00
James Cole
1ac95b6fa7 Move getCash lower, add IBAN as name when no name is submitted. Search will pick up the rest. 2020-05-09 18:03:32 +02:00
James Cole
6757b6211d https://github.com/firefly-iii/firefly-iii/issues/3348 2020-05-09 17:47:25 +02:00
James Cole
4dfb78837e Slightly smarter menu. 2020-05-09 14:51:04 +02:00
James Cole
af50ad3db4 New action 2020-05-09 14:47:47 +02:00
James Cole
01cb94aabc Room for better translation 2020-05-09 14:47:33 +02:00
James Cole
5ffc5060b9 Merge branch 'develop' of github.com:firefly-iii/firefly-iii into develop 2020-05-07 06:44:27 +02:00
James Cole
43d7c956a4 Fix #3344 2020-05-07 06:44:01 +02:00
James Cole
863b07951a Fix #3339 2020-05-05 20:53:56 +02:00
James Cole
2101717edb Can return without split. 2020-05-05 20:32:04 +02:00
James Cole
e7ac1476c5 Merge pull request #3336 from maksimkurb/develop
WIP: Fix #3335 Reconciliation account currency is wrong when base account has non-default currency
2020-05-05 20:30:05 +02:00
Maxim Kurbatov
5d24c1fee1 #3335 Update language files for reconciliation account name 2020-05-05 22:10:11 +04:00
Maxim Kurbatov
53eb863f9d Fix #3335 Reconciliation account currency is wrong when base account has non-default currency 2020-05-05 22:07:42 +04:00
James Cole
3c3ba637b5 Make sure demo user can't upload attachments. 2020-05-05 07:44:33 +02:00
James Cole
be8286b15c Fix some things in recurring transactions. 2020-05-04 10:26:01 +02:00
James Cole
6f6087995d Add info on expired recurring transactions. 2020-05-04 10:12:38 +02:00
James Cole
2ff8a35171 Add debug. 2020-05-04 10:06:13 +02:00
James Cole
7f8ed7abb6 Merge branch 'release/5.2.5' 2020-05-04 08:59:55 +02:00
James Cole
2ff0c37c12 Merge tag '5.2.5' into develop
5.2.5
2020-05-04 08:59:55 +02:00
James Cole
c593936b32 New meta files for upcoming release. 2020-05-04 08:56:51 +02:00
James Cole
c54204ede9 Fix #3330 2020-05-03 19:38:03 +02:00
James Cole
d9132bbee3 Merge pull request #3323 from lguima/feature/page-icons-update
Update the pages and blocks icons
2020-05-02 17:11:27 +02:00
Lucas Guima
c1be98762e Update the Tags icon around the site 2020-05-01 18:55:23 -03:00
Lucas Guima
c2733e2a8f Update the Categories icon around the site 2020-05-01 18:54:08 -03:00
Lucas Guima
722cf9b4fe Update the Reports icon around the site 2020-05-01 17:39:43 -03:00
Lucas Guima
01e31e73e0 Update the Piggy Banks icon around the site 2020-05-01 17:32:35 -03:00
Lucas Guima
dba7d05296 Update the Budgets icon around the site 2020-05-01 17:29:50 -03:00
James Cole
a8709a4a45 update env. 2020-05-01 20:16:34 +02:00
James Cole
49b1435cba Fix cron controller. #3318 2020-05-01 17:51:30 +02:00
James Cole
6a25b41952 Update translations. 2020-05-01 17:50:53 +02:00
James Cole
c561f99de6 Fix #3321 2020-05-01 17:47:56 +02:00
James Cole
9d9053d828 Fix #3314 2020-05-01 06:24:24 +02:00
James Cole
c3c9a2f3c0 Slightly different icon. 2020-04-30 06:27:53 +02:00
James Cole
28465142e9 Menu changelog. 2020-04-30 06:27:10 +02:00
James Cole
5473a618c9 Warn about locale things. 2020-04-30 06:27:01 +02:00
James Cole
ac1a8d8053 Merge pull request #3305 from lguima/feature/menu-reordering
Menu reordering
2020-04-30 06:10:51 +02:00
James Cole
15ae9203b6 Fix #3307 2020-04-29 06:37:02 +02:00
Lucas Guima
9dfc2ae20b Use the translatable string for Tools 2020-04-28 20:59:22 -03:00
Lucas Guima
230de7cbdd Use the translatable string for Classification 2020-04-28 20:59:10 -03:00
Lucas Guima
feaa003a52 Use the translatable string for Others 2020-04-28 20:57:36 -03:00
Lucas Guima
295d01dc16 Use the translatable string for Automation 2020-04-28 20:57:11 -03:00
Lucas Guima
cbf1fde45e Use the translatable string for Accounting 2020-04-28 20:56:19 -03:00
Lucas Guima
6cc47287d3 Use the translatable string for Financial control 2020-04-28 20:55:52 -03:00
Lucas Guima
4bbe728376 Create translatable string for Classification 2020-04-28 20:43:02 -03:00
Lucas Guima
427a90ec85 Create translatable string for Others 2020-04-28 20:32:10 -03:00
Lucas Guima
55ddb26dac Create translatable string for Automation 2020-04-28 20:16:25 -03:00
Lucas Guima
891777f079 Create translatable string for Accounting 2020-04-28 20:14:15 -03:00
Lucas Guima
1655286b67 Create translatable string for Financial control 2020-04-28 20:10:43 -03:00
Lucas Guima
dd81636bf2 Add an icon for each sub-menu item 2020-04-28 19:29:53 -03:00
Lucas Guima
56a43a707d Update the icon of Bills menu item 2020-04-28 19:07:44 -03:00
Lucas Guima
c2f92c6e45 Update the icon of Options menu item 2020-04-28 19:07:11 -03:00
Lucas Guima
61bd2dc840 Revert "Move the menu items Profile and Logout to the user menu in the main header"
This reverts commit f6a675f2e2.
2020-04-28 18:55:49 -03:00
Lucas Guima
f6a675f2e2 Move the menu items Profile and Logout to the user menu in the main header 2020-04-27 22:29:14 -03:00
Lucas Guima
85b43055a7 Put the menu sub-items text inside a span tag 2020-04-27 21:44:23 -03:00
Lucas Guima
e63f7bcc70 Put a space between arguments 2020-04-27 21:41:00 -03:00
Lucas Guima
384dd37430 Update the menu item Logout 2020-04-27 21:37:20 -03:00
Lucas Guima
c6b336171c Update the menu item Options 2020-04-27 21:36:47 -03:00
Lucas Guima
d2f4399a1a Update the menu item Tools (Import and export) 2020-04-27 21:30:12 -03:00
Lucas Guima
02d1bc093c Update the menu item Reports 2020-04-27 21:14:38 -03:00
Lucas Guima
420e493987 Group Categories and Tags under the new menu item Classification 2020-04-27 21:12:23 -03:00
Lucas Guima
0a15479bff Update the menu item Accounts 2020-04-27 20:53:25 -03:00
Lucas Guima
a9b76a3679 Add menu header for Others 2020-04-27 20:44:39 -03:00
Lucas Guima
d1a3cd9044 Move and update the menu item Automation (Money management) 2020-04-27 20:41:40 -03:00
Lucas Guima
81735d59f8 Move and update the menu item Transactions 2020-04-27 19:59:53 -03:00
Lucas Guima
1d8da7f9f0 Add menu header for Accounting 2020-04-27 18:35:52 -03:00
Lucas Guima
c5f0684030 Place the menu item Piggy banks after Bills 2020-04-26 18:11:20 -03:00
Lucas Guima
25867adcb9 Move the menu item Piggy banks 2020-04-26 18:10:22 -03:00
Lucas Guima
d55694cd68 Move the menu item Bills 2020-04-26 18:07:08 -03:00
Lucas Guima
05f069d61e Move the menu item Budgets 2020-04-26 18:00:28 -03:00
Lucas Guima
5f6e7ad138 Add menu header for Financial Control 2020-04-26 17:50:42 -03:00
Lucas Guima
61bc38921e Update Dashboard menu item 2020-04-26 17:48:13 -03:00
Lucas Guima
94c660545d Update indentation 2020-04-26 17:44:07 -03:00
James Cole
4d3907948d Merge tag '5.2.4' into develop
5.2.4
2020-04-26 10:02:43 +02:00
James Cole
e6442dd8af Merge branch 'release/5.2.4' 2020-04-26 10:02:42 +02:00
James Cole
7905e0bd70 Bump to 5.2.4 2020-04-26 10:02:25 +02:00
James Cole
f4b1da352d no message 2020-04-26 07:08:33 +02:00
James Cole
0d33348941 Fix #3251 2020-04-26 06:57:59 +02:00
James Cole
c7c875e95f Fix #3251 2020-04-26 06:54:12 +02:00
James Cole
19d24b3e2a Clean up templates 2020-04-26 06:45:42 +02:00
James Cole
8fed6b6657 Fix #3287 2020-04-22 09:28:20 +02:00
James Cole
b5eafa1910 Merge tag '5.2.3' into develop
5.2.3
2020-04-22 06:33:23 +02:00
James Cole
c15501937f Merge branch 'release/5.2.3' 2020-04-22 06:33:22 +02:00
James Cole
4c743bd5b0 Update meta files for new release. 2020-04-22 06:29:34 +02:00
James Cole
44289cbd95 Fix #3284 2020-04-22 06:09:29 +02:00
James Cole
b2f1642cfe Fix #3281 2020-04-21 08:17:31 +02:00
James Cole
341ef0220c New translations. 2020-04-19 11:07:14 +02:00
James Cole
9df88115bc Update packages. 2020-04-19 11:05:14 +02:00
James Cole
c7273edb5e Updated language strings and meta config. 2020-04-19 06:52:12 +02:00
James Cole
c398aa2b69 Add support for British English and allow the user to set a locale. 2020-04-19 06:51:40 +02:00
James Cole
aa786eaaf3 Clean up language setting. 2020-04-19 06:11:49 +02:00
James Cole
e58a5e12d6 Fix #3270 2020-04-19 06:10:49 +02:00
James Cole
12b3575c5c Fix method reference 2020-04-19 06:09:55 +02:00
James Cole
3ca186dc8f Remove unused method. 2020-04-19 06:09:43 +02:00
James Cole
1535f596f6 Add two new support functions. 2020-04-19 06:07:43 +02:00
James Cole
2cc326caa1 Fix #3272 2020-04-19 06:05:39 +02:00
James Cole
43436ae942 Fix issue with casting. 2020-04-19 06:00:11 +02:00
James Cole
fbfd8475de Fix #3264 2020-04-15 16:17:45 +02:00
James Cole
405752f353 Merge tag '5.2.2' into develop
5.2.2
2020-04-14 21:01:03 +02:00
232 changed files with 7219 additions and 1726 deletions

View File

@@ -22,15 +22,15 @@ APP_KEY=SomeRandomStringOf32CharsExactly
# If text is still in English, remember that not everything may have been translated.
DEFAULT_LANGUAGE=en_US
# The locale defines how numbers are formatted.
# by default this value is the same as whatever the language is.
DEFAULT_LOCALE=equal
# Change this value to your preferred time zone.
# Example: Europe/Amsterdam
# For a list of supported time zones, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=Europe/Amsterdam
# This variable must match your installation's external address but keep in mind that
# it's only used on the command line as a fallback value.
APP_URL=http://localhost
# TRUSTED_PROXIES is a useful variable when using Docker and/or a reverse proxy.
# Set it to ** and reverse proxies work just fine.
TRUSTED_PROXIES=
@@ -191,6 +191,7 @@ ADLDAP_AUTH_FIELD=distinguishedname
# Will allow SSO if your server provides an AUTH_USER field.
# You can set the following variables from a file by appending them with _FILE:
WINDOWS_SSO_ENABLED=false
WINDOWS_SSO_DISCOVER=samaccountname
WINDOWS_SSO_KEY=AUTH_USER
@@ -270,3 +271,15 @@ IS_SANDSTORM=false
IS_DOCKER=false
IS_HEROKU=false
BUNQ_USE_SANDBOX=false
#
# If you have trouble configuring your Firefly III installation, DON'T BOTHER setting this variable.
# 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 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
#
# If you're stuck I understand you get desperate but look SOMEWHERE ELSE.
#
APP_URL=http://localhost

View File

@@ -16,10 +16,10 @@ I am running Firefly III version x.x.x, and my problem is:
<!-- Please add extra info here, such as OS, browser, and the output from the /debug page of your Firefly III installation (click the version at the bottom). -->
**Bonus points**
<!-- Earn bonus points by checking the boxes -->
<!-- Before you submit, verify the following please: -->
- [ ] Nobody reported this bug before
- [ ] I have added a stack trace from my log files <!-- (see https://bit.ly/FF3-get-debug-info) -->
- [ ] I have added a screenshot.
- [ ] I was able to replicate it on the demo site https://demo.firefly-iii.org/
<!-- - [ ] I donated money (this is a joke :wink:)-->
- I searched and nobody reported this bug before
- I have added a stack trace from my log files <!-- (see https://bit.ly/FF3-get-debug-info) -->
- I have added a screenshot.
- I was able to replicate it on the demo site https://demo.firefly-iii.org/
<!-- - I donated money (this is a joke ;)-->

View File

@@ -16,8 +16,8 @@ I am running Firefly III version x.x.x.
<!-- Complete the following checklist for bonus points -->
- [ ] I have read the FAQ at https://bit.ly/FF3-FAQ
- [ ] I added a screenshot
- [ ] I added log files <!-- (see https://bit.ly/FF3-get-debug-info) -->
- [ ] I was able to replicate the issue on the demo site.
<!-- - [ ] I donated money (this is a joke :wink:)-->
- I have read the FAQ at https://bit.ly/FF3-FAQ
- I added a screenshot
- I added log files <!-- (see https://bit.ly/FF3-get-debug-info) -->
- I was able to replicate the issue on the demo site.
<!-- - I donated money (this is a joke :wink:)-->

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/node_modules
/frontend/node_modules
/public/hot
/public/storage
/storage/*.key

View File

@@ -23,10 +23,12 @@ declare(strict_types=1);
namespace FireflyIII\Api\V1\Controllers;
use FireflyIII\Api\V1\Middleware\ApiDemoUser;
use FireflyIII\Api\V1\Requests\AttachmentStoreRequest;
use FireflyIII\Api\V1\Requests\AttachmentUpdateRequest;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
use FireflyIII\Http\Middleware\IsDemoUser;
use FireflyIII\Models\Attachment;
use FireflyIII\Repositories\Attachment\AttachmentRepositoryInterface;
use FireflyIII\Transformers\AttachmentTransformer;
@@ -58,6 +60,7 @@ class AttachmentController extends Controller
public function __construct()
{
parent::__construct();
$this->middleware(ApiDemoUser::class)->except(['delete', 'download', 'show', 'index']);
$this->middleware(
function ($request, $next) {
/** @var User $user */
@@ -65,6 +68,7 @@ class AttachmentController extends Controller
$this->repository = app(AttachmentRepositoryInterface::class);
$this->repository->setUser($user);
return $next($request);
}
);

View File

@@ -269,6 +269,7 @@ class TransactionController extends Controller
*
* @param TransactionStoreRequest $request
*
* @throws FireflyException
* @return JsonResponse
*/
public function store(TransactionStoreRequest $request): JsonResponse
@@ -283,7 +284,7 @@ class TransactionController extends Controller
try {
$transactionGroup = $this->groupRepository->store($data);
} catch (DuplicateTransactionException $e) {
Log::warning('Caught a duplicate. Return error message.');
Log::warning('Caught a duplicate transaction. Return error message.');
// return bad validation message.
// TODO use Laravel's internal validation thing to do this.
$response = [
@@ -326,7 +327,7 @@ class TransactionController extends Controller
$selectedGroup = $collector->getGroups()->first();
if (null === $selectedGroup) {
throw new NotFoundHttpException(); // @codeCoverageIgnore
throw new FireflyException('Cannot find transaction. Possibly, a rule deleted this transaction after its creation.');
}
/** @var TransactionGroupTransformer $transformer */
$transformer = app(TransactionGroupTransformer::class);

View File

@@ -0,0 +1,61 @@
<?php
/**
* ApiDemoUser.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\Api\V1\Middleware;
use Closure;
use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User;
use Illuminate\Http\Request;
/**
* Class ApiDemoUser.
*/
class ApiDemoUser
{
/**
* Handle an incoming request.
*
* @param Request $request
* @param Closure $next
*
* @return mixed
*/
public function handle(Request $request, Closure $next)
{
/** @var User $user */
$user = $request->user();
if (null === $user) {
return $next($request);
}
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
if ($repository->hasRole($user, 'demo')) {
return response('', 403);
}
return $next($request);
}
}

View File

@@ -87,7 +87,7 @@ class Handler extends ExceptionHandler
);
}
return response()->json(['message' => 'Internal Firefly III Exception. See log files.', 'exception' => get_class($exception)], 500);
return response()->json(['message' => sprintf('Internal Firefly III Exception: %s', $exception->getMessage()), 'exception' => get_class($exception)], 500);
}
if ($exception instanceof NotFoundHttpException) {

View File

@@ -428,7 +428,7 @@ class TransactionJournalFactory
->first();
}
if (null !== $result) {
Log::warning('Found a duplicate!');
Log::warning(sprintf('Found a duplicate in errorIfDuplicate because hash %s is not unique!', $hash));
throw new DuplicateTransactionException(sprintf('Duplicate of transaction #%d.', $result->transactionJournal->transaction_group_id));
}
}
@@ -607,7 +607,7 @@ class TransactionJournalFactory
// validate source account.
$sourceId = isset($data['source_id']) ? (int) $data['source_id'] : null;
$sourceName = $data['source_name'] ?? null;
$sourceName = isset($data['source_name']) ? (string) $data['source_name'] : null;
$validSource = $this->accountValidator->validateSource($sourceId, $sourceName, null);
// do something with result:
@@ -617,7 +617,7 @@ class TransactionJournalFactory
Log::debug('Source seems valid.');
// validate destination account
$destinationId = isset($data['destination_id']) ? (int) $data['destination_id'] : null;
$destinationName = (string)($data['destination_name'] ?? null);
$destinationName = isset($data['destination_name']) ? (string) $data['destination_name'] : null;
$validDestination = $this->accountValidator->validateDestination($destinationId, $destinationName, null);
// do something with result:
if (false === $validDestination) {

View File

@@ -154,14 +154,14 @@ class MonthReportGenerator implements ReportGeneratorInterface
$journals[$index]['invoice_date'] = $journalRepository->getMetaDateById($journal['transaction_journal_id'], 'invoice_date');
}
$locale = app('steam')->getLocale();
$return = [
'journals' => $journals,
'currency' => $currency,
'exists' => count($journals) > 0,
'end' => $this->end->formatLocalized((string) trans('config.month_and_day')),
'end' => $this->end->formatLocalized((string) trans('config.month_and_day', [], $locale)),
'endBalance' => app('steam')->balance($account, $this->end),
'dayBefore' => $date->formatLocalized((string) trans('config.month_and_day')),
'dayBefore' => $date->formatLocalized((string) trans('config.month_and_day', [], $locale)),
'dayBeforeBalance' => $dayBeforeBalance,
];

View File

@@ -118,6 +118,7 @@ class UserEventHandler
if ($repository->hasRole($user, 'demo')) {
// set user back to English.
app('preferences')->setForUser($user, 'language', 'en_US');
app('preferences')->setForUser($user, 'locale', 'equal');
app('preferences')->mark();
}

View File

@@ -54,6 +54,7 @@ class VersionCheckEventHandler
$value = (int) $permission->data;
if (1 !== $value) {
Log::info('Update check is not enabled.');
$this->warnToCheckForUpdates($event);
return;
}
@@ -85,4 +86,36 @@ class VersionCheckEventHandler
session()->flash($release['level'], $release['message']);
app('fireflyconfig')->set('last_update_check', time());
}
/**
* @param RequestedVersionCheckStatus $event
*/
protected function warnToCheckForUpdates(RequestedVersionCheckStatus $event): void
{
/** @var UserRepositoryInterface $repository */
$repository = app(UserRepositoryInterface::class);
/** @var User $user */
$user = $event->user;
if (!$repository->hasRole($user, 'owner')) {
Log::debug('User is not admin, done.');
return;
}
/** @var Configuration $lastCheckTime */
$lastCheckTime = app('fireflyconfig')->get('last_update_warning', time());
$now = time();
$diff = $now - $lastCheckTime->data;
Log::debug(sprintf('Last warning time is %d, current time is %d, difference is %d', $lastCheckTime->data, $now, $diff));
if ($diff < 604800 * 4) {
Log::debug(sprintf('Warned about updates less than four weeks ago (on %s).', date('Y-m-d H:i:s', $lastCheckTime->data)));
return;
}
// last check time was more than a week ago.
Log::debug('Have warned about a new version in four weeks!');
session()->flash('info', (string) trans('firefly.disabled_but_check'));
app('fireflyconfig')->set('last_update_warning', time());
}
}

View File

@@ -91,8 +91,9 @@ trait TimeCollection
if ($end < $start) {
[$start, $end] = [$end, $start];
}
$startStr = $start->format('Y-m-d H:i:s');
$endStr = $end->format('Y-m-d H:i:s');
// always got to end of day / start of day for ranges.
$startStr = $start->format('Y-m-d 00:00:00');
$endStr = $end->format('Y-m-d 23:59:59');
$this->query->where('transaction_journals.date', '>=', $startStr);
$this->query->where('transaction_journals.date', '<=', $endStr);

View File

@@ -31,16 +31,11 @@ use FireflyIII\Helpers\Collector\Extensions\AmountCollection;
use FireflyIII\Helpers\Collector\Extensions\CollectorProperties;
use FireflyIII\Helpers\Collector\Extensions\MetaCollection;
use FireflyIII\Helpers\Collector\Extensions\TimeCollection;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Budget;
use FireflyIII\Models\Category;
use FireflyIII\Models\Tag;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
@@ -526,7 +521,7 @@ class GroupCollector implements GroupCollectorInterface
}
// or parse the rest.
$journalId = (int) $augumentedJournal->transaction_journal_id;
$groups[$groupId]['count']++;
if (isset($groups[$groupId]['transactions'][$journalId])) {
// append data to existing group + journal (for multiple tags or multiple attachments)
@@ -536,6 +531,7 @@ class GroupCollector implements GroupCollectorInterface
if (!isset($groups[$groupId]['transactions'][$journalId])) {
// create second, third, fourth split:
$groups[$groupId]['count']++;
$groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedJournal($augumentedJournal);
}
}

View File

@@ -109,15 +109,13 @@ class NetWorth implements NetWorthInterface
Log::debug(sprintf('Balance is %s', $balance));
// if the account is a credit card, subtract the virtual balance from the balance,
// to better reflect that this is not money that is actually "yours".
$role = (string) $this->accountRepository->getMetaValue($account, 'account_role');
// always subtract virtual balance.
$virtualBalance = (string) $account->virtual_balance;
if ('ccAsset' === $role && '' !== $virtualBalance && (float) $virtualBalance > 0) {
if ('' !== $virtualBalance) {
$balance = bcsub($balance, $virtualBalance);
}
Log::debug(sprintf('Balance corrected to %s', $balance));
Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance));
if (!isset($netWorth[$currencyId])) {
$netWorth[$currencyId] = '0';

View File

@@ -151,7 +151,12 @@ class CreateController extends Controller
// store attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($account, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -190,9 +190,13 @@ class EditController extends Controller
app('preferences')->mark();
// store new attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($account, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -101,7 +101,8 @@ class TelemetryController extends Controller
app('view')->share('subTitleIcon', 'fa-eye');
app('view')->share('subTitle', (string) trans('firefly.telemetry_admin_index'));
$version = config('firefly.version');
$enabled = config('firefly.telemetry', false);
$enabled = config('firefly.send_telemetry', false) && config('firefly.feature_flags.telemetry');
$count = $this->repository->count();
return view('admin.telemetry.index', compact('version', 'enabled', 'count'));

View File

@@ -82,14 +82,8 @@ class LoginController extends Controller
Log::channel('audit')->info(sprintf('User is trying to login using "%s"', $request->get('email')));
Log::info(sprintf('User is trying to login.'));
if ('ldap' === config('auth.providers.users.driver')) {
/**
* Temporary bug fix for something that doesn't seem to work in
* AdLdap.
*/
$schema = config('ldap.connections.default.schema');
/** @var Adldap\Connections\Provider $provider */
Adldap::getProvider('default')->setSchema(new $schema);
Adldap::getProvider('default');
}
$this->validateLogin($request);

View File

@@ -383,7 +383,12 @@ class BillController extends Controller
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
@@ -410,7 +415,12 @@ class BillController extends Controller
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($bill, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
// flash messages
if (count($this->attachments->getMessages()->get('attachments')) > 0) {

View File

@@ -59,7 +59,7 @@ class AvailableBudgetController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
$this->currencyRepos = app(CurrencyRepositoryInterface::class);
@@ -157,6 +157,7 @@ class AvailableBudgetController extends Controller
*/
public function edit(AvailableBudget $availableBudget, Carbon $start, Carbon $end)
{
$availableBudget->amount = round($availableBudget->amount, $availableBudget->transactionCurrency->decimal_places);
return view('budgets.available-budgets.edit', compact('availableBudget', 'start', 'end'));
}

View File

@@ -70,7 +70,7 @@ class BudgetLimitController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->repository = app(BudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->blRepository = app(BudgetLimitRepositoryInterface::class);

View File

@@ -58,7 +58,7 @@ class CreateController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->repository = app(BudgetRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
@@ -130,9 +130,13 @@ class CreateController extends Controller
app('preferences')->mark();
// store attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($budget, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -54,7 +54,7 @@ class DeleteController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->repository = app(BudgetRepositoryInterface::class);
return $next($request);

View File

@@ -59,7 +59,7 @@ class EditController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->repository = app(BudgetRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
@@ -137,9 +137,13 @@ class EditController extends Controller
$redirect = redirect($this->getPreviousUri('budgets.edit.uri'));
// store new attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($budget, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -72,7 +72,7 @@ class IndexController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->repository = app(BudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);
@@ -86,7 +86,6 @@ class IndexController extends Controller
}
/**
* TODO the "budgeted" progress bar doesn't update.
* Show all budgets.
*
* @param Request $request
@@ -98,6 +97,7 @@ class IndexController extends Controller
*/
public function index(Request $request, Carbon $start = null, Carbon $end = null)
{
Log::debug('Start of IndexController::index()');
// collect some basic vars:
$range = app('preferences')->get('viewRange', '1M')->data;
$start = $start ?? session('start', Carbon::now()->startOfMonth());
@@ -105,16 +105,18 @@ class IndexController extends Controller
$defaultCurrency = app('amount')->getDefaultCurrency();
$budgeted = '0';
$spent = '0';
Log::debug(sprintf('1) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// new period stuff:
$periodTitle = app('navigation')->periodShow($start, $range);
$prevLoop = $this->getPreviousPeriods($start, $range);
$nextLoop = $this->getNextPeriods($start, $range);
Log::debug(sprintf('2) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// get all available budgets.
$ab = $this->abRepository->get($start, $end);
$availableBudgets = [];
Log::debug(sprintf('3) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// for each, complement with spent amount:
/** @var AvailableBudget $entry */
foreach ($ab as $entry) {
@@ -131,6 +133,7 @@ class IndexController extends Controller
$array['budgeted'] = $budgeted;
$availableBudgets[] = $array;
unset($spentArr);
Log::debug(sprintf('4) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
if (0 === count($availableBudgets)) {
@@ -139,6 +142,7 @@ class IndexController extends Controller
$spentArr = $this->opsRepository->sumExpenses($start, $end, null, null, $defaultCurrency);
$spent = $spentArr[$defaultCurrency->id]['sum'] ?? '0';
unset($spentArr);
Log::debug(sprintf('5) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
// count the number of enabled currencies. This determines if we display a "+" button.
@@ -148,11 +152,12 @@ class IndexController extends Controller
// number of days for consistent budgeting.
$activeDaysPassed = $this->activeDaysPassed($start, $end); // see method description.
$activeDaysLeft = $this->activeDaysLeft($start, $end); // see method description.
Log::debug(sprintf('Start: %s, end: %s', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
Log::debug(sprintf('6) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// get all budgets, and paginate them into $budgets.
$collection = $this->repository->getActiveBudgets();
$budgets = [];
Log::debug(sprintf('7) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
// complement budget with budget limits in range, and expenses in currency X in range.
/** @var Budget $current */
@@ -163,18 +168,22 @@ class IndexController extends Controller
$array['attachments'] = $this->repository->getAttachments($current);
$array['auto_budget'] = $this->repository->getAutoBudget($current);
$budgetLimits = $this->blRepository->getBudgetLimits($current, $start, $end);
Log::debug(sprintf('8) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
/** @var BudgetLimit $limit */
foreach ($budgetLimits as $limit) {
$currency = $limit->transactionCurrency ?? $defaultCurrency;
$array['budgeted'][] = [
'id' => $limit->id,
'amount' => round($limit->amount, $currency->decimal_places),
'start_date' => $limit->start_date->formatLocalized($this->monthAndDayFormat),
'end_date' => $limit->end_date->formatLocalized($this->monthAndDayFormat),
'in_range' => $limit->start_date->isSameDay($start) && $limit->end_date->isSameDay($end),
'currency_id' => $currency->id,
'currency_symbol' => $currency->symbol,
'currency_name' => $currency->name,
'currency_decimal_places' => $currency->decimal_places,
];
Log::debug(sprintf('9) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
/** @var TransactionCurrency $currency */
@@ -185,6 +194,7 @@ class IndexController extends Controller
$array['spent'][$currency->id]['currency_id'] = $currency->id;
$array['spent'][$currency->id]['currency_symbol'] = $currency->symbol;
$array['spent'][$currency->id]['currency_decimal_places'] = $currency->decimal_places;
Log::debug(sprintf('10) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
}
}
$budgets[] = $array;
@@ -192,7 +202,7 @@ class IndexController extends Controller
// get all inactive budgets, and simply list them:
$inactive = $this->repository->getInactiveBudgets();
Log::debug(sprintf('11) Start is "%s", end is "%s"', $start->format('Y-m-d H:i:s'), $end->format('Y-m-d H:i:s')));
return view(
'budgets.index',

View File

@@ -64,7 +64,7 @@ class ShowController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->journalRepos = app(JournalRepositoryInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);

View File

@@ -57,7 +57,7 @@ class CreateController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->repository = app(CategoryRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
@@ -102,9 +102,13 @@ class CreateController extends Controller
app('preferences')->mark();
// store attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($category, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -53,7 +53,7 @@ class DeleteController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->repository = app(CategoryRepositoryInterface::class);
return $next($request);

View File

@@ -59,7 +59,7 @@ class EditController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->repository = app(CategoryRepositoryInterface::class);
$this->attachments = app(AttachmentHelperInterface::class);
@@ -107,9 +107,13 @@ class EditController extends Controller
app('preferences')->mark();
// store new attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($category, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -53,7 +53,7 @@ class IndexController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->repository = app(CategoryRepositoryInterface::class);
return $next($request);

View File

@@ -59,7 +59,7 @@ class NoCategoryController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->journalRepos = app(JournalRepositoryInterface::class);
return $next($request);

View File

@@ -58,7 +58,7 @@ class ShowController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.categories'));
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('mainTitleIcon', 'fa-bookmark');
$this->repository = app(CategoryRepositoryInterface::class);
return $next($request);
@@ -84,7 +84,7 @@ class ShowController extends Controller
$start = $start ?? session('start', Carbon::now()->startOfMonth());
/** @var Carbon $end */
$end = $end ?? session('end', Carbon::now()->endOfMonth());
$subTitleIcon = 'fa-bar-chart';
$subTitleIcon = 'fa-bookmark';
$page = (int) $request->get('page');
$attachments = $this->repository->getAttachments($category);
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
@@ -122,7 +122,7 @@ class ShowController extends Controller
public function showAll(Request $request, Category $category)
{
// default values:
$subTitleIcon = 'fa-bar-chart';
$subTitleIcon = 'fa-bookmark';
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$start = null;

View File

@@ -571,6 +571,7 @@ class AccountController extends Controller
*/
private function periodByCurrency(Carbon $start, Carbon $end, Account $account, TransactionCurrency $currency): array
{
$locale = app('steam')->getLocale();
$step = $this->calculateStep($start, $end);
$result = [
'label' => sprintf('%s (%s)', $account->name, $currency->symbol),
@@ -582,7 +583,7 @@ class AccountController extends Controller
switch ($step) {
case '1D':
// per day the entire period, balance for every day.
$format = (string) trans('config.month_and_day');
$format = (string) trans('config.month_and_day', [], $locale);
$range = app('steam')->balanceInRange($account, $start, $end, $currency);
$previous = array_values($range)[0];
while ($end >= $current) {

View File

@@ -111,6 +111,7 @@ class BillController extends Controller
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$locale = app('steam')->getLocale();
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
@@ -131,7 +132,7 @@ class BillController extends Controller
];
foreach ($journals as $journal) {
$date = $journal['date']->formatLocalized((string) trans('config.month_and_day'));
$date = $journal['date']->formatLocalized((string) trans('config.month_and_day', [], $locale));
$chartData[0]['entries'][$date] = $bill->amount_min; // minimum amount of bill
$chartData[1]['entries'][$date] = $bill->amount_max; // maximum amount of bill

View File

@@ -179,14 +179,14 @@ class BudgetController extends Controller
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$locale = app('steam')->getLocale();
$entries = [];
$amount = $budgetLimit->amount;
$budgetCollection = new Collection([$budget]);
while ($start <= $end) {
$spent = $this->opsRepository->spentInPeriod($budgetCollection, new Collection, $start, $start);
$amount = bcadd($amount, $spent);
$format = $start->formatLocalized((string)trans('config.month_and_day'));
$format = $start->formatLocalized((string)trans('config.month_and_day', [], $locale));
$entries[$format] = $amount;
$start->addDay();

View File

@@ -76,6 +76,7 @@ class PiggyBankController extends Controller
}
$set = $repository->getEvents($piggyBank);
$set = $set->reverse();
$locale =app('steam')->getLocale();
// get first event or start date of piggy bank or today
$startDate = $piggyBank->start_date ?? new Carbon;
@@ -99,7 +100,7 @@ class PiggyBankController extends Controller
}
);
$currentSum = $filtered->sum('amount');
$label = $oldest->formatLocalized((string) trans('config.month_and_day'));
$label = $oldest->formatLocalized((string) trans('config.month_and_day', [], $locale));
$chartData[$label] = $currentSum;
$oldest = app('navigation')->addPeriod($oldest, $step, 0);
}
@@ -110,7 +111,7 @@ class PiggyBankController extends Controller
}
);
$finalSum = $finalFiltered->sum('amount');
$finalLabel = $today->formatLocalized((string) trans('config.month_and_day'));
$finalLabel = $today->formatLocalized((string) trans('config.month_and_day', [], $locale));
$chartData[$finalLabel] = $finalSum;
$data = $this->generator->singleSet($piggyBank->name, $chartData);

View File

@@ -79,6 +79,7 @@ class ReportController extends Controller
if ($cache->has()) {
return response()->json($cache->get()); // @codeCoverageIgnore
}
$locale = app('steam')->getLocale();
$current = clone $start;
$chartData = [];
/** @var NetWorthInterface $helper */
@@ -110,7 +111,7 @@ class ReportController extends Controller
/** @var array $netWorthItem */
foreach ($result as $netWorthItem) {
$currencyId = $netWorthItem['currency']->id;
$label = $current->formatLocalized((string) trans('config.month_and_day'));
$label = $current->formatLocalized((string) trans('config.month_and_day', [], $locale));
if (!isset($chartData[$currencyId])) {
$chartData[$currencyId] = [
'label' => 'Net worth in ' . $netWorthItem['currency']->name,

View File

@@ -82,21 +82,11 @@ class TransactionController extends Controller
foreach ($result as $journal) {
$budget = $journal['budget_name'] ?? (string) trans('firefly.no_budget');
$title = sprintf('%s (%s)', $budget, $journal['currency_symbol']);
// key => [value => x, 'currency_symbol' => 'x']
$data[$title] = $data[$title] ?? [
'amount' => '0',
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
if (null !== $journal['foreign_amount']) {
$title = sprintf('%s (%s)', $budget, $journal['foreign_currency_symbol']);
$data[$title] = $data[$title] ?? [
'amount' => $journal['foreign_amount'],
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
}
}
$chart = $this->generator->multiCurrencyPieChart($data);
$cache->store($chart);
@@ -150,21 +140,12 @@ class TransactionController extends Controller
foreach ($result as $journal) {
$category = $journal['category_name'] ?? (string) trans('firefly.no_category');
$title = sprintf('%s (%s)', $category, $journal['currency_symbol']);
// key => [value => x, 'currency_symbol' => 'x']
$data[$title] = $data[$title] ?? [
'amount' => '0',
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
if (null !== $journal['foreign_amount']) {
$title = sprintf('%s (%s)', $category, $journal['foreign_currency_symbol']);
$data[$title] = $data[$title] ?? [
'amount' => $journal['foreign_amount'],
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
}
}
$chart = $this->generator->multiCurrencyPieChart($data);
$cache->store($chart);
@@ -223,15 +204,6 @@ class TransactionController extends Controller
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
if (null !== $journal['foreign_amount']) {
$title = sprintf('%s (%s)', $name, $journal['foreign_currency_symbol']);
$data[$title] = $data[$title] ?? [
'amount' => $journal['foreign_amount'],
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
}
}
$chart = $this->generator->multiCurrencyPieChart($data);
$cache->store($chart);
@@ -291,14 +263,6 @@ class TransactionController extends Controller
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['amount']);
if (null !== $journal['foreign_amount']) {
$title = sprintf('%s (%s)', $name, $journal['foreign_currency_symbol']);
$data[$title] = $data[$title] ?? [
'amount' => $journal['foreign_amount'],
'currency_symbol' => $journal['currency_symbol'],
];
$data[$title]['amount'] = bcadd($data[$title]['amount'], $journal['foreign_amount']);
}
}
$chart = $this->generator->multiCurrencyPieChart($data);
$cache->store($chart);

View File

@@ -85,17 +85,20 @@ class Controller extends BaseController
$this->middleware(
function ($request, $next) {
$locale = app('steam')->getLocale();
// translations for specific strings:
$this->monthFormat = (string) trans('config.month');
$this->monthAndDayFormat = (string) trans('config.month_and_day');
$this->dateTimeFormat = (string) trans('config.date_time');
$this->monthFormat = (string) trans('config.month', [], $locale);
$this->monthAndDayFormat = (string) trans('config.month_and_day', [], $locale);
$this->dateTimeFormat = (string) trans('config.date_time', [], $locale);
// get shown-intro-preference:
if (auth()->check()) {
$language = $this->getLanguage();
$language = app('steam')->getLanguage();
$locale = app('steam')->getLocale();
$page = $this->getPageName();
$shownDemo = $this->hasSeenDemo();
app('view')->share('language', $language);
app('view')->share('locale', $locale);
app('view')->share('shownDemo', $shownDemo);
app('view')->share('current_route_name', $page);
app('view')->share('original_route_name', Route::currentRouteName());

View File

@@ -126,7 +126,6 @@ class DebugController extends Controller
$phpOs = str_replace($search, $replace, PHP_OS);
$interface = PHP_SAPI;
$now = Carbon::now()->format('Y-m-d H:i:s e');
$extensions = implode(', ', get_loaded_extensions());
$drivers = implode(', ', DB::availableDrivers());
$currentDriver = DB::getDriverName();
$userAgent = $request->header('user-agent');
@@ -143,7 +142,7 @@ class DebugController extends Controller
// set languages, see what happens:
$original = setlocale(LC_ALL, 0);
$localeAttempts = [];
$parts = explode(',', (string) trans('config.locale'));
$parts = app('steam')->getLocaleArray(app('steam')->getLocale());
foreach ($parts as $code) {
$code = trim($code);
$localeAttempts[$code] = var_export(setlocale(LC_ALL, $code), true);
@@ -178,7 +177,6 @@ class DebugController extends Controller
'debug',
compact(
'phpVersion',
'extensions',
'localeAttempts',
'appEnv',
'appDebug',

View File

@@ -63,7 +63,7 @@ class BudgetController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.budgets'));
app('view')->share('mainTitleIcon', 'fa-tasks');
app('view')->share('mainTitleIcon', 'fa-pie-chart');
$this->repository = app(BudgetRepositoryInterface::class);
$this->opsRepository = app(OperationsRepositoryInterface::class);
$this->abRepository = app(AvailableBudgetRepositoryInterface::class);

View File

@@ -151,6 +151,7 @@ class RecurrenceController extends Controller
$today = Carbon::now()->startOfDay();
$date = Carbon::createFromFormat('Y-m-d', $string)->startOfDay();
$preSelected = (string) $request->get('pre_select');
$locale = app('steam')->getLocale();
Log::debug(sprintf('date = %s, today = %s. date > today? %s', $date->toAtomString(), $today->toAtomString(), var_export($date > $today, true)));
Log::debug(sprintf('past = true? %s', var_export('true' === (string) $request->get('past'), true)));
@@ -163,7 +164,7 @@ class RecurrenceController extends Controller
$dayOfWeek = (string) trans(sprintf('config.dow_%s', $date->dayOfWeekIso));
$ndom = sprintf('ndom,%s,%s', $date->weekOfMonth, $date->dayOfWeekIso);
$yearly = sprintf('yearly,%s', $date->format('Y-m-d'));
$yearlyDate = $date->formatLocalized((string) trans('config.month_and_day_no_year'));
$yearlyDate = $date->formatLocalized((string) trans('config.month_and_day_no_year', [], $locale));
$result = [
'daily' => ['label' => (string) trans('firefly.recurring_daily'), 'selected' => 0 === strpos($preSelected, 'daily')],
$weekly => ['label' => (string) trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]),

View File

@@ -71,7 +71,7 @@ class PiggyBankController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.piggyBanks'));
app('view')->share('mainTitleIcon', 'fa-sort-amount-asc');
app('view')->share('mainTitleIcon', 'fa-bullseye');
$this->attachments = app(AttachmentHelperInterface::class);
$this->piggyRepos = app(PiggyBankRepositoryInterface::class);
@@ -456,7 +456,12 @@ class PiggyBankController extends Controller
// store attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($piggyBank, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
@@ -495,7 +500,12 @@ class PiggyBankController extends Controller
// store new attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($piggyBank, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -31,6 +31,7 @@ use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
use JsonException;
/**
* Class PreferencesController.
@@ -90,8 +91,9 @@ class PreferencesController extends Controller
$viewRange = $viewRangePref->data;
$frontPageAccounts = app('preferences')->get('frontPageAccounts', $accountIds);
$language = app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data;
$language = app('steam')->getLanguage();
$languages = config('firefly.languages');
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
$listPageSize = app('preferences')->get('listPageSize', 50)->data;
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
$fiscalYearStartStr = app('preferences')->get('fiscalYearStart', '01-01')->data;
@@ -100,6 +102,15 @@ class PreferencesController extends Controller
ksort($languages);
// list of locales also has "equal" which makes it equal to whatever the language is.
try {
$locales = json_decode(file_get_contents(resource_path(sprintf('lang/%s/locales.json', $language))), true, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
Log::error($e->getMessage());
$locales = [];
}
$locales = ['equal' => (string) trans('firefly.equal_to_language')] + $locales;
// an important fallback is that the frontPageAccount array gets refilled automatically
// when it turns up empty.
if (0 === count($frontPageAccounts->data)) {
@@ -113,6 +124,8 @@ class PreferencesController extends Controller
'groupedAccounts',
'frontPageAccounts',
'languages',
'locales',
'locale',
'tjOptionalFields',
'viewRange',
'customFiscalYear',
@@ -172,6 +185,13 @@ class PreferencesController extends Controller
session()->flash('info', 'All translations are supplied by volunteers. There might be errors and mistakes. I appreciate your feedback.');
}
// same for locale:
if (!auth()->user()->hasRole('demo')) {
/** @var Preference $currentLocale */
$locale = $request->get('locale');
app('preferences')->set('locale', $locale);
}
// optional fields for transactions:
$setOptions = $request->get('tj');
$optionalTj = [

View File

@@ -85,6 +85,8 @@ class IndexController extends Controller
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$collection = $this->recurring->get();
$today = new Carbon;
$year = new Carbon;
// split collection
$total = $collection->count();
@@ -98,8 +100,7 @@ class IndexController extends Controller
$recurring = [];
/** @var Recurrence $recurrence */
foreach ($recurrences as $recurrence) {
$today = new Carbon;
$year = new Carbon;
$year->addYear();
if ($recurrence->first_date > $today) {
$today = clone $recurrence->first_date;
@@ -110,7 +111,18 @@ class IndexController extends Controller
$array['first_date'] = new Carbon($array['first_date']);
$array['repeat_until'] = null === $array['repeat_until'] ? null : new Carbon($array['repeat_until']);
$array['latest_date'] = null === $array['latest_date'] ? null : new Carbon($array['latest_date']);
$array['occurrences'] = array_slice($this->recurring->getOccurrencesInRange($recurrence->recurrenceRepetitions->first(), $today, $year), 0, 1);
// make carbon objects out of occurrences
foreach ($array['repetitions'] as $repIndex => $repetition) {
foreach ($repetition['occurrences'] as $occIndex => $occurrence) {
$array['repetitions'][$repIndex]['occurrences'][$occIndex] = new Carbon($occurrence);
}
}
//if (0 !== $recurrence->recurrenceRepetitions->count()) {
//$array['ocurrences'] = array_slice($this->recurring->getOccurrencesInRange($recurrence->recurrenceRepetitions->first(), $today, $year), 0, 1);
//}
$recurring[] = $array;
}
$paginator = new LengthAwarePaginator($recurring, $total, $pageSize, $page);
@@ -118,7 +130,7 @@ class IndexController extends Controller
$this->verifyRecurringCronJob();
return view('recurring.index', compact('paginator', 'page', 'pageSize', 'total'));
return view('recurring.index', compact('paginator', 'today', 'page', 'pageSize', 'total'));
}
}

View File

@@ -84,6 +84,8 @@ class ShowController extends Controller
$array = $transformer->transform($recurrence);
$groups = $this->recurring->getTransactions($recurrence);
$today = new Carbon;
$array['repeat_until'] = null !== $array['repeat_until'] ? new Carbon($array['repeat_until']) : null;
// transform dates back to Carbon objects:
foreach ($array['repetitions'] as $index => $repetition) {
@@ -94,6 +96,6 @@ class ShowController extends Controller
$subTitle = (string) trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]);
return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups'));
return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups','today'));
}
}

View File

@@ -301,7 +301,6 @@ class BudgetController extends Controller
$report[$budgetId]['currencies'][$currencyId]['sum_pct'] = $pct;
}
}
return view('reports.budget.partials.budgets', compact('sums', 'report'));
}
@@ -317,6 +316,7 @@ class BudgetController extends Controller
*/
public function general(Collection $accounts, Carbon $start, Carbon $end)
{
$report = [
'budgets' => [],
'sums' => [],
@@ -488,6 +488,7 @@ class BudgetController extends Controller
foreach ($expenses as $currency) {
foreach ($currency['budgets'] as $budget) {
$count = 0;
$total = '0';
foreach ($budget['transaction_journals'] as $journal) {
$count++;
$key = sprintf('%d-%d', $budget['id'], $currency['currency_id']);
@@ -506,7 +507,7 @@ class BudgetController extends Controller
$report[$key]['entries'][$dateKey] = $report[$key] ['entries'][$dateKey] ?? '0';
$report[$key]['entries'][$dateKey] = bcadd($journal['amount'], $report[$key] ['entries'][$dateKey]);
$report[$key]['sum'] = bcadd($report[$key] ['sum'], $journal['amount']);
$report[$key]['avg'] = bcdiv($report[$key]['sum'], (string) $count);
$report[$key]['avg'] = bcdiv($report[$key]['sum'], (string) count($periods));
}
}
}

View File

@@ -64,7 +64,7 @@ class ReportController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.reports'));
app('view')->share('mainTitleIcon', 'fa-line-chart');
app('view')->share('mainTitleIcon', 'fa-bar-chart');
app('view')->share('subTitleIcon', 'fa-calendar');
$this->helper = app(ReportHelperInterface::class);
$this->repository = app(BudgetRepositoryInterface::class);

View File

@@ -91,6 +91,7 @@ class IndexController extends Controller
$user = auth()->user();
$this->createDefaultRuleGroup();
$this->createDefaultRule();
$this->ruleGroupRepos->resetRuleGroupOrder();
$ruleGroups = $this->ruleGroupRepos->getRuleGroupsWithRules($user);
return view('rules.index', compact('ruleGroups'));

View File

@@ -41,6 +41,7 @@ class CronController
{
$results = [];
$results[] = $this->runRecurring();
$results[] = $this->runAutoBudget();
return implode("<br>\n", $results);
}

View File

@@ -60,7 +60,7 @@ class TagController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.tags'));
app('view')->share('mainTitleIcon', 'fa-tags');
app('view')->share('mainTitleIcon', 'fa-tag');
$this->attachments = app(AttachmentHelperInterface::class);
$this->repository = app(TagRepositoryInterface::class);
@@ -321,7 +321,12 @@ class TagController extends Controller
// store attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($result, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
@@ -359,7 +364,12 @@ class TagController extends Controller
// store new attachment(s):
/** @var array $files */
$files = $request->hasFile('attachments') ? $request->file('attachments') : null;
if (null !== $files && !auth()->user()->hasRole('demo')) {
$this->attachments->saveAttachmentsForModel($tag, $files);
}
if (null !== $files && auth()->user()->hasRole('demo')) {
session()->flash('info',(string)trans('firefly.no_att_demo_user'));
}
if (count($this->attachments->getMessages()->get('attachments')) > 0) {
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore

View File

@@ -56,7 +56,7 @@ class BulkController extends Controller
function ($request, $next) {
$this->repository = app(JournalRepositoryInterface::class);
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
app('view')->share('mainTitleIcon', 'fa-exchange');
return $next($request);
}

View File

@@ -206,9 +206,9 @@ class ConvertController extends Controller
// double check its not an empty string.
$sourceId = '' === $sourceId || null === $sourceId ? null : (int) $sourceId;
$sourceName = '' === $sourceName ? null : $sourceName;
$sourceName = '' === $sourceName ? null : (string) $sourceName;
$destinationId = '' === $destinationId || null === $destinationId ? null : (int) $destinationId;
$destinationName = (string)('' === $destinationName ? null : $destinationName);
$destinationName = '' === $destinationName ? null : (string) $destinationName;
$validSource = $validator->validateSource($sourceId, $sourceName, null);
$validDestination = $validator->validateDestination($destinationId, $destinationName, null);

View File

@@ -49,7 +49,7 @@ class CreateController extends Controller
$this->middleware(
static function ($request, $next) {
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
app('view')->share('mainTitleIcon', 'fa-exchange');
return $next($request);
}

View File

@@ -56,7 +56,7 @@ class DeleteController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
app('view')->share('mainTitleIcon', 'fa-exchange');
$this->repository = app(TransactionGroupRepositoryInterface::class);

View File

@@ -52,7 +52,7 @@ class EditController extends Controller
static function ($request, $next) {
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
app('view')->share('mainTitleIcon', 'fa-exchange');
return $next($request);
}

View File

@@ -56,8 +56,8 @@ class IndexController extends Controller
// translations:
$this->middleware(
function ($request, $next) {
app('view')->share('mainTitleIcon', 'fa-credit-card');
app('view')->share('title', (string) trans('firefly.accounts'));
app('view')->share('mainTitleIcon', 'fa-exchange');
app('view')->share('title', (string) trans('firefly.transactions'));
$this->repository = app(JournalRepositoryInterface::class);
@@ -88,7 +88,9 @@ class IndexController extends Controller
$end = session('end');
}
if (null === $end) {
$end = session('end'); // @codeCoverageIgnore
// get last transaction ever?
$last = $this->repository->getLast();
$end = $last ? $last->date : session('end');
}
[$start, $end] = $end < $start ? [$end, $start] : [$start, $end];
@@ -134,14 +136,15 @@ class IndexController extends Controller
$repository = app(JournalRepositoryInterface::class);
$subTitleIcon = config('firefly.transactionIconsByWhat.' . $objectType);
$types = config('firefly.transactionTypesByWhat.' . $objectType);
$subTitleIcon = config('firefly.transactionIconsByType.' . $objectType);
$types = config('firefly.transactionTypesByType.' . $objectType);
$page = (int) $request->get('page');
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
$path = route('transactions.index.all', [$objectType]);
$first = $repository->firstNull();
$start = null === $first ? new Carbon : $first->date;
$end = new Carbon;
$last = $this->repository->getLast();
$end = $last ? $last->date : new Carbon;
$subTitle = (string) trans('firefly.all_' . $objectType);
/** @var GroupCollectorInterface $collector */

View File

@@ -57,7 +57,7 @@ class LinkController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
app('view')->share('mainTitleIcon', 'fa-exchange');
$this->journalRepository = app(JournalRepositoryInterface::class);
$this->repository = app(LinkTypeRepositoryInterface::class);

View File

@@ -62,7 +62,7 @@ class MassController extends Controller
$this->middleware(
function ($request, $next) {
app('view')->share('title', (string) trans('firefly.transactions'));
app('view')->share('mainTitleIcon', 'fa-repeat');
app('view')->share('mainTitleIcon', 'fa-exchange');
$this->repository = app(JournalRepositoryInterface::class);
return $next($request);

View File

@@ -26,6 +26,7 @@ use App;
use Carbon\Carbon;
use Closure;
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\Support\Http\Controllers\RequestInformation;
use Illuminate\Http\Request;
/**
@@ -33,6 +34,7 @@ use Illuminate\Http\Request;
*/
class Range
{
use RequestInformation;
/**
* Handle an incoming request.
*
@@ -72,16 +74,16 @@ class Range
*/
private function configureView(): void
{
$pref = app('preferences')->get('language', config('firefly.default_language', 'en_US'));
/** @noinspection NullPointerExceptionInspection */
$lang = $pref->data;
App::setLocale($lang);
Carbon::setLocale(substr($lang, 0, 2));
$locale = explode(',', (string) trans('config.locale'));
$locale = array_map('trim', $locale);
// get locale preference:
$language = app('steam')->getLanguage();
$locale = app('steam')->getLocale();
App::setLocale($language);
Carbon::setLocale(substr($locale, 0, 2));
setlocale(LC_TIME, $locale);
$moneyResult = setlocale(LC_MONETARY, $locale);
$localeArray = app('steam')->getLocaleArray($locale);
setlocale(LC_TIME, $localeArray);
$moneyResult = setlocale(LC_MONETARY, $localeArray);
// send error to view if could not set money format
if (false === $moneyResult) {
@@ -89,12 +91,12 @@ class Range
}
// save some formats:
$monthAndDayFormat = (string) trans('config.month_and_day');
$dateTimeFormat = (string) trans('config.date_time');
$monthAndDayFormat = (string) trans('config.month_and_day', [], $locale);
$dateTimeFormat = (string) trans('config.date_time', [], $locale);
$defaultCurrency = app('amount')->getDefaultCurrency();
// also format for moment JS:
$madMomentJS = (string) trans('config.month_and_day_moment_js');
$madMomentJS = (string) trans('config.month_and_day_moment_js', [], $locale);
app('view')->share('madMomentJS', $madMomentJS);
app('view')->share('monthAndDayFormat', $monthAndDayFormat);

View File

@@ -58,7 +58,7 @@ class SecureHeaders
"base-uri 'self'",
"font-src 'self' data:",
"connect-src 'self'",
sprintf("img-src 'self' data: https://api.tiles.mapbox.com %s", $trackingScriptSrc),
sprintf("img-src 'self' data: https://a.tile.openstreetmap.org https://b.tile.openstreetmap.org https://c.tile.openstreetmap.org https://api.tiles.mapbox.com %s", $trackingScriptSrc),
"manifest-src 'self'",
];

View File

@@ -53,7 +53,7 @@ class PiggyBankFormRequest extends Request
'account_id' => $this->integer('account_id'),
'targetamount' => $this->string('targetamount'),
'targetdate' => $this->date('targetdate'),
'notes' => $this->string('notes'),
'notes' => $this->nlString('notes'),
];
}

View File

@@ -297,8 +297,9 @@ class Request extends FormRequest
$latitudeKey = $this->getLocationKey($prefix, 'latitude');
$zoomLevelKey = $this->getLocationKey($prefix, 'zoom_level');
$hasLocationKey = $this->getLocationKey($prefix, 'has_location');
$hasLocation = $this->boolean($hasLocationKey);
// for a POST (store, all fields must be present and accounted for:
// for a POST (store), all fields must be present and accounted for:
if (
('POST' === $this->method() && $this->routeIs('*.store'))
&& ($this->has($longitudeKey) && $this->has($latitudeKey) && $this->has($zoomLevelKey))
@@ -322,12 +323,14 @@ class Request extends FormRequest
$data['latitude'] = $this->nullableString($latitudeKey);
$data['zoom_level'] = $this->nullableString($zoomLevelKey);
}
if (null === $data['longitude'] || null === $data['latitude'] || null === $data['zoom_level']) {
Log::debug('One of the fields is NULL, wont save.');
if (false === $hasLocation || null === $data['longitude'] || null === $data['latitude'] || null === $data['zoom_level']) {
Log::debug('One of the fields is NULL or hasLocation is false, wont save.');
$data['store_location'] = false;
$data['update_location'] = false;
$data['update_location'] = true; // update is always true, but the values are null:
$data['longitude'] = null;
$data['latitude'] = null;
$data['zoom_level'] = null;
}
Log::debug(sprintf('Returning longitude: "%s", latitude: "%s", zoom level: "%s"', $data['longitude'], $data['latitude'], $data['zoom_level']));
return $data;

View File

@@ -83,7 +83,7 @@ class AccountMeta extends Model
*/
public function getDataAttribute($value)
{
return json_decode($value);
return json_decode($value, true, 512, JSON_THROW_ON_ERROR);
}
/**

View File

@@ -78,13 +78,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
* @property-read \Illuminate\Database\Eloquent\Collection|Location[] $locations
* @property-read int|null $locations_count
* @property-read int|null $transaction_journals_count
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property string $tagMode
* @property string|null $description
* @property float|null $latitude
* @property float|null $longitude
* @property int|null $zoomLevel
*/
class Tag extends Model
{

View File

@@ -26,6 +26,7 @@ use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
use Laravel\Passport\Passport;
use URL;
use Adldap\Laravel\Middleware\WindowsAuthenticate;
/**
* @codeCoverageIgnore
@@ -44,6 +45,9 @@ class AppServiceProvider extends ServiceProvider
if ('heroku' === config('app.env')) {
URL::forceScheme('https');
}
if (config('ldap_auth.identifiers.windows.enabled', false)) {
$this->app['router']->pushMiddlewareToGroup('web', WindowsAuthenticate::class);
}
}
/**

View File

@@ -28,6 +28,7 @@ use FireflyIII\Factory\AccountFactory;
use FireflyIII\Models\Account;
use FireflyIII\Models\AccountMeta;
use FireflyIII\Models\AccountType;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Location;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Models\TransactionGroup;
@@ -39,6 +40,7 @@ use FireflyIII\User;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class AccountRepository.
@@ -473,27 +475,34 @@ class AccountRepository implements AccountRepositoryInterface
if (AccountType::ASSET !== $account->accountType->type) {
throw new FireflyException(sprintf('%s is not an asset account.', $account->name));
}
$name = trans('firefly.reconciliation_account_name', ['name' => $account->name]);
$currency = $this->getAccountCurrency($account) ?? app('amount')->getDefaultCurrency();
$name = trans('firefly.reconciliation_account_name', ['name' => $account->name, 'currency' => $currency->code]);
/** @var AccountType $type */
$type = AccountType::where('type', AccountType::RECONCILIATION)->first();
$accounts = $this->user->accounts()->where('account_type_id', $type->id)->get();
// TODO no longer need to loop like this
$current = $this->user->accounts()->where('account_type_id', $type->id)
->where('name', $name)
->first();
/** @var Account $current */
foreach ($accounts as $current) {
if ($current->name === $name) {
if (null !== $current) {
return $current;
}
}
$data = [
'account_type_id' => null,
'account_type' => AccountType::RECONCILIATION,
'active' => true,
'name' => $name,
'currency_id' => $currency->id,
'currency_code' => $currency->code,
];
/** @var AccountFactory $factory */
$factory = app(AccountFactory::class);
$factory->setUser($account->user);
$account = $factory->findOrCreate($name, $type->type);
return $account;
return $factory->create($data);
}
/**
@@ -651,7 +660,22 @@ class AccountRepository implements AccountRepositoryInterface
*/
public function getAttachments(Account $account): Collection
{
return $account->attachments()->get();
$set = $account->attachments()->get();
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
/**

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\BillFactory;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Bill;
use FireflyIII\Models\Note;
use FireflyIII\Models\Transaction;
@@ -39,6 +40,7 @@ use Illuminate\Database\Query\JoinClause;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class BillRepository.
@@ -164,7 +166,22 @@ class BillRepository implements BillRepositoryInterface
*/
public function getAttachments(Bill $bill): Collection
{
return $bill->attachments()->get();
$set = $bill->attachments()->get();
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
/**

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use DB;
use Exception;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\AutoBudget;
use FireflyIII\Models\Budget;
use FireflyIII\Models\BudgetLimit;
@@ -38,6 +39,7 @@ use FireflyIII\User;
use Illuminate\Database\QueryException;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class BudgetRepository.
@@ -79,6 +81,8 @@ class BudgetRepository implements BudgetRepositoryInterface
$budget->order = $index + 1;
$budget->save();
}
// other budgets, set to 0.
$this->user->budgets()->where('active', 0)->update(['order' => 0]);
return true;
}
@@ -187,12 +191,12 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
public function getActiveBudgets(): Collection
{
//throw new \RuntimeException;
/** @var Collection $set */
$set = $this->user->budgets()->where('active', 1)
->orderBy('order', 'DESC')
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')
->get();
return $set;
}
@@ -202,7 +206,7 @@ class BudgetRepository implements BudgetRepositoryInterface
public function getBudgets(): Collection
{
/** @var Collection $set */
$set = $this->user->budgets()->orderBy('order', 'DESC')
$set = $this->user->budgets()->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->get();
return $set;
@@ -227,7 +231,7 @@ class BudgetRepository implements BudgetRepositoryInterface
{
/** @var Collection $set */
$set = $this->user->budgets()
->orderBy('order', 'DESC')
->orderBy('order', 'ASC')
->orderBy('name', 'ASC')->where('active', 0)->get();
return $set;
@@ -277,11 +281,13 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
public function store(array $data): Budget
{
$order = $this->getMaxOrder();
try {
$newBudget = Budget::create(
[
'user_id' => $this->user->id,
'name' => $data['name'],
'order' => $order + 1,
]
);
} catch (QueryException $e) {
@@ -485,6 +491,26 @@ class BudgetRepository implements BudgetRepositoryInterface
*/
public function getAttachments(Budget $budget): Collection
{
return $budget->attachments()->get();
$set = $budget->attachments()->get();
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
public function getMaxOrder(): int
{
return (int)$this->user->budgets()->max('order');
}
}

View File

@@ -58,6 +58,11 @@ interface BudgetRepositoryInterface
*/
public function destroyAutoBudget(Budget $budget): void;
/**
* @return int
*/
public function getMaxOrder(): int;
/**
* @return bool

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use DB;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Factory\CategoryFactory;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Category;
use FireflyIII\Models\RecurrenceTransactionMeta;
use FireflyIII\Models\RuleAction;
@@ -34,6 +35,7 @@ use FireflyIII\Services\Internal\Update\CategoryUpdateService;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class CategoryRepository.
@@ -380,6 +382,21 @@ class CategoryRepository implements CategoryRepositoryInterface
*/
public function getAttachments(Category $category): Collection
{
return $category->attachments()->get();
$set = $category->attachments()->get();
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
}

View File

@@ -23,12 +23,14 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Journal;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\PiggyBankEvent;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class JournalAPIRepository
@@ -74,7 +76,22 @@ class JournalAPIRepository implements JournalAPIRepositoryInterface
*/
public function getAttachments(TransactionJournal $journal): Collection
{
return $journal->attachments;
$set = $journal->attachments;
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
/**

View File

@@ -407,4 +407,19 @@ class JournalRepository implements JournalRepositoryInterface
return $transaction->account;
}
/**
* @return TransactionJournal|null
*/
public function getLast(): ?TransactionJournal
{
/** @var TransactionJournal $entry */
$entry = $this->user->transactionJournals()->orderBy('date', 'DESC')->first(['transaction_journals.*']);
$result = null;
if (null !== $entry) {
$result = $entry;
}
return $result;
}
}

View File

@@ -37,6 +37,10 @@ use Illuminate\Support\Collection;
*/
interface JournalRepositoryInterface
{
/**
* @return TransactionJournal|null
*/
public function getLast(): ?TransactionJournal;
/**
* TODO maybe create JSON repository?
@@ -44,6 +48,7 @@ interface JournalRepositoryInterface
* Search in journal descriptions.
*
* @param string $search
*
* @return Collection
*/
public function searchJournalDescriptions(string $search): Collection;

View File

@@ -142,6 +142,9 @@ trait ModifiesPiggyBanks
*/
public function createEvent(PiggyBank $piggyBank, string $amount): PiggyBankEvent
{
if (0 === bccomp('0', $amount)) {
return new PiggyBankEvent;
}
/** @var PiggyBankEvent $event */
$event = PiggyBankEvent::create(['date' => Carbon::now(), 'amount' => $amount, 'piggy_bank_id' => $piggyBank->id]);
@@ -219,11 +222,12 @@ trait ModifiesPiggyBanks
if (1 === bccomp($amount, $max)) {
$amount = $max;
}
$difference = bcsub($amount, $repetition->currentamount);
$repetition->currentamount = $amount;
$repetition->save();
// create event
$this->createEvent($piggyBank, $amount);
$this->createEvent($piggyBank, $difference);
return $piggyBank;
}

View File

@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\PiggyBank;
use Carbon\Carbon;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Note;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\PiggyBankRepetition;
@@ -33,6 +34,7 @@ use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class PiggyBankRepository.
@@ -374,6 +376,21 @@ class PiggyBankRepository implements PiggyBankRepositoryInterface
*/
public function getAttachments(PiggyBank $piggyBank): Collection
{
return $piggyBank->attachments()->get();
$set = $piggyBank->attachments()->get();
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
}

View File

@@ -535,6 +535,31 @@ class RecurringRepository implements RecurringRepositoryInterface
// filter out all the weekend days:
$occurrences = $this->filterWeekends($repetition, $occurrences);
// filter out everything if "repeat_until" is set.
$repeatUntil = $repetition->recurrence->repeat_until;
$occurrences = $this->filterMaxDate($repeatUntil, $occurrences);
return $occurrences;
}
/**
* @param Carbon|null $max
* @param array $occurrences
*
* @return array
*/
private function filterMaxDate(?Carbon $max, array $occurrences): array
{
if (null === $max) {
return $occurrences;
}
$filtered = [];
foreach ($occurrences as $date) {
if ($date->lte($max)) {
$filtered[] = $date;
}
}
return $filtered;
}
}

View File

@@ -92,12 +92,18 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
{
$this->user->ruleGroups()->whereNotNull('deleted_at')->update(['order' => 0]);
$set = $this->user->ruleGroups()->where('active', 1)->orderBy('order', 'ASC')->get();
$set = $this->user
->ruleGroups()
->orderBy('order', 'ASC')->get();
$count = 1;
/** @var RuleGroup $entry */
foreach ($set as $entry) {
$entry->order = $count;
$entry->save();
// also update rules in group.
$this->resetRulesInGroupOrder($entry);
++$count;
}
@@ -209,18 +215,16 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
public function getRuleGroupsWithRules(User $user): Collection
{
return $user->ruleGroups()
->orderBy('active', 'DESC')
->orderBy('order', 'ASC')
->with(
[
'rules' => function (HasMany $query) {
$query->orderBy('active', 'DESC');
'rules' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
'rules.ruleTriggers' => function (HasMany $query) {
'rules.ruleTriggers' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
'rules.ruleActions' => function (HasMany $query) {
'rules.ruleActions' => static function (HasMany $query) {
$query->orderBy('order', 'ASC');
},
]
@@ -353,4 +357,5 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
{
return $this->user->ruleGroups()->where('title', $title)->first();
}
}

View File

@@ -26,6 +26,7 @@ use Carbon\Carbon;
use DB;
use FireflyIII\Factory\TagFactory;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Models\Attachment;
use FireflyIII\Models\Location;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\RuleTrigger;
@@ -34,6 +35,7 @@ use FireflyIII\Models\TransactionType;
use FireflyIII\User;
use Illuminate\Support\Collection;
use Log;
use Storage;
/**
* Class TagRepository.
@@ -549,7 +551,21 @@ class TagRepository implements TagRepositoryInterface
*/
public function getAttachments(Tag $tag): Collection
{
return $tag->attachments()->get();
$set= $tag->attachments()->get();
/** @var Storage $disk */
$disk = Storage::disk('upload');
$set = $set->each(
static function (Attachment $attachment) use ($disk) {
$notes = $attachment->notes()->first();
$attachment->file_exists = $disk->exists($attachment->fileName());
$attachment->notes = $notes ? $notes->text : '';
return $attachment;
}
);
return $set;
}
/**

View File

@@ -346,6 +346,7 @@ class TransactionGroupRepository implements TransactionGroupRepositoryInterface
Log::warning('Group repository caught group factory with a duplicate exception!');
throw new DuplicateTransactionException($e->getMessage());
} catch(FireflyException $e) {
Log::warning('Group repository caught group factory with an exception!');
Log::error($e->getMessage());
Log::error($e->getTraceAsString());
throw new FireflyException($e->getMessage());

View File

@@ -95,8 +95,6 @@ class JournalDestroyService
// update events
$journal->piggyBankEvents()->update(['transaction_journal_id' => null]);
$journal->delete();
} catch (Exception $e) {
Log::error(sprintf('Could not delete bill: %s', $e->getMessage())); // @codeCoverageIgnore

View File

@@ -112,8 +112,8 @@ trait JournalServiceTrait
$result = $this->findAccountById($data, $expectedTypes[$transactionType]);
$result = $this->findAccountByName($result, $data, $expectedTypes[$transactionType]);
$result = $this->findAccountByIban($result, $data, $expectedTypes[$transactionType]);
$result = $this->getCashAccount($result, $data, $expectedTypes[$transactionType]);
$result = $this->createAccount($result, $data, $expectedTypes[$transactionType][0]);
$result = $this->getCashAccount($result, $data, $expectedTypes[$transactionType]);
return $result;
}
@@ -301,7 +301,7 @@ trait JournalServiceTrait
{
// third attempt, find by IBAN
if (null === $account && null !== $data['iban']) {
Log::debug('Found nothing by account name.');
Log::debug(sprintf('Found nothing by account iban "%s".', $data['iban']));
// find by preferred type.
$source = $this->accountRepository->findByIbanNull($data['iban'], [$types[0]]);
// or any expected type.
@@ -347,13 +347,28 @@ trait JournalServiceTrait
{
Log::debug('Now in createAccount()', $data);
// return new account.
if (null !== $account) {
Log::debug(
sprintf(
'Was also given %s account #%d ("%s") so will simply return that.',
$account->accountType->type, $account->id, $account->name
)
);
}
if (null === $account) {
$data['name'] = $data['name'] ?? '(no name)';
// final attempt, create it.
if (AccountType::ASSET === $preferredType) {
throw new FireflyException('TransactionFactory: Cannot create asset account with these values', $data);
}
// fix name of account if only IBAN is given:
if ('' === (string) $data['name'] && '' !== (string) $data['iban']) {
Log::debug(sprintf('Account name is now IBAN ("%s")', $data['iban']));
$data['name'] = $data['iban'];
}
$data['name'] = $data['name'] ?? '(no name)';
$account = $this->accountRepository->store(
[

View File

@@ -325,9 +325,11 @@ class Amount
*/
public function getLocaleInfo(): array
{
$locale = explode(',', (string) trans('config.locale'));
$locale = array_map('trim', $locale);
setlocale(LC_MONETARY, $locale);
// get config from preference, not from translation:
$locale = app('steam')->getLocale();
$array = app('steam')->getLocaleArray($locale);
setlocale(LC_MONETARY, $array);
$info = localeconv();
// correct variables
$info['n_cs_precedes'] = $this->getLocaleField($info, 'n_cs_precedes');

View File

@@ -48,7 +48,7 @@ class JournalList implements BinderInterface
// get the journals by using the collector.
/** @var GroupCollectorInterface $collector */
$collector = app(GroupCollectorInterface::class);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER]);
$collector->setTypes([TransactionType::WITHDRAWAL, TransactionType::DEPOSIT, TransactionType::TRANSFER, TransactionType::RECONCILIATION]);
$collector->withCategoryInformation()->withBudgetInformation()->withTagInformation()->withAccountInformation();
$collector->setJournalIds($list);
$result = $collector->getExtractedJournals();

View File

@@ -86,6 +86,7 @@ class RecurringCronjob extends AbstractCronjob
$job->setForce($this->force);
$job->handle();
app('fireflyconfig')->set('last_rt_job', (int)$this->date->format('U'));
Log::info(sprintf('Marked the last time this job has run as "%s" (%d)',$this->date->format('Y-m-d H:i:s'),(int)$this->date->format('U')));
Log::info('Done with recurring cron job task.');
}
}

View File

@@ -557,7 +557,7 @@ class ExportDataGenerator
$recurrence->description,
$recurrence->first_date ? $recurrence->first_date->format('Y-m-d') : null,
$recurrence->repeat_until ? $recurrence->repeat_until->format('Y-m-d') : null,
$recurrence->latest_date ? $recurrence->repeat_until->format('Y-m-d') : null,
$recurrence->latest_date ? $recurrence->latest_date->format('Y-m-d') : null,
$recurrence->repetitions,
$recurrence->apply_rules,
$recurrence->active,

View File

@@ -61,6 +61,7 @@ trait ChartGeneration
return $cache->get(); // @codeCoverageIgnore
}
Log::debug('Regenerate chart.account.account-balance-chart from scratch.');
$locale = app('steam')->getLocale();
/** @var GeneratorInterface $generator */
$generator = app(GeneratorInterface::class);
@@ -89,7 +90,7 @@ trait ChartGeneration
$previous = array_values($range)[0];
while ($currentStart <= $end) {
$format = $currentStart->format('Y-m-d');
$label = $currentStart->formatLocalized((string)trans('config.month_and_day'));
$label = $currentStart->formatLocalized((string)trans('config.month_and_day', [], $locale));
$balance = isset($range[$format]) ? round($range[$format], 12) : $previous;
$previous = $balance;
$currentStart->addDay();

View File

@@ -24,6 +24,7 @@ declare(strict_types=1);
namespace FireflyIII\Support\Http\Controllers;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Support\Cronjobs\AutoBudgetCronjob;
use FireflyIII\Support\Cronjobs\RecurringCronjob;
/**
@@ -50,4 +51,23 @@ trait CronRunner
return 'The recurring transaction cron job fired successfully.';
}
/**
* @return string
*/
protected function runAutoBudget(): string
{
/** @var AutoBudgetCronjob $autoBudget */
$autoBudget = app(AutoBudgetCronjob::class);
try {
$result = $autoBudget->fire();
} catch (FireflyException $e) {
return $e->getMessage();
}
if (false === $result) {
return 'The auto budget cron job did not fire.';
}
return 'The auto budget cron job fired successfully.';
}
}

View File

@@ -119,19 +119,6 @@ trait RequestInformation
return '<p>' . trans('firefly.route_has_no_help') . '</p>'; // @codeCoverageIgnore
}
/**
* Get user's language.
*
* @return string
*/
protected function getLanguage(): string // get preference
{
/** @var string $language */
$language = app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data;
return $language;
}
/**
* Get a list of triggers.
*

View File

@@ -287,10 +287,11 @@ class Navigation
*/
public function listOfPeriods(Carbon $start, Carbon $end): array
{
$locale = app('steam')->getLocale();
// define period to increment
$increment = 'addDay';
$format = $this->preferredCarbonFormat($start, $end);
$displayFormat = (string)trans('config.month_and_day');
$displayFormat = (string)trans('config.month_and_day', [], $locale);
// increment by month (for year)
if ($start->diffInMonths($end) > 1) {
$increment = 'addMonth';
@@ -391,13 +392,14 @@ class Navigation
*/
public function preferredCarbonLocalizedFormat(Carbon $start, Carbon $end): string
{
$format = (string)trans('config.month_and_day');
$locale = app('steam')->getLocale();
$format = (string)trans('config.month_and_day', [], $locale);
if ($start->diffInMonths($end) > 1) {
$format = (string)trans('config.month');
$format = (string)trans('config.month', [], $locale);
}
if ($start->diffInMonths($end) > 12) {
$format = (string)trans('config.year');
$format = (string)trans('config.year', [], $locale);
}
return $format;

View File

@@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
namespace FireflyIII\Support;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use Log;
/**
* Class ParseDateString
*/
class ParseDateString
{
private $keywords
= [
'today',
'yesterday',
'tomorrow',
'start of this week',
'end of this week',
'start of this month',
'end of this month',
'start of this quarter',
'end of this quarter',
'start of this year',
'end of this year',
];
/**
* @param string $date
*
* @return Carbon
*/
public function parseDate(string $date): Carbon
{
// parse keywords:
if (in_array($date, $this->keywords, true)) {
return $this->parseKeyword($date);
}
// if regex for YYYY-MM-DD:
$pattern = '/^(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][\d]|3[01])$/';
if (preg_match($pattern, $date)) {
return $this->parseDefaultDate($date);
}
// if + or -:
if (0 === strpos($date, '+') || 0 === strpos($date, '-')) {
return $this->parseRelativeDate($date);
}
throw new FireflyException('Not recognised.');
}
/**
* @param string $date
*
* @return Carbon
*/
private function parseDefaultDate(string $date): Carbon
{
return Carbon::createFromFormat('Y-m-d', $date);
}
/**
* @param string $keyword
*
* @return Carbon
*/
private function parseKeyword(string $keyword): Carbon
{
$today = Carbon::today()->startOfDay();
switch ($keyword) {
default:
case 'today':
return $today;
case 'yesterday':
return $today->subDay();
case 'tomorrow':
return $today->addDay();
case 'start of this week':
return $today->startOfWeek();
case 'end of this week':
return $today->endOfWeek();
case 'start of this month':
return $today->startOfMonth();
case 'end of this month':
return $today->endOfMonth();
case 'start of this quarter':
return $today->startOfQuarter();
case 'end of this quarter':
return $today->endOfQuarter();
case 'start of this year':
return $today->startOfYear();
case 'end of this year':
return $today->endOfYear();
}
}
/**
* @param string $date
*
* @return Carbon
*/
private function parseRelativeDate(string $date): Carbon
{
Log::debug(sprintf('Now in parseRelativeDate("%s")', $date));
$parts = explode(' ', $date);
$today = Carbon::today()->startOfDay();
$functions = [
[
'd' => 'subDays',
'w' => 'subWeeks',
'm' => 'subMonths',
'q' => 'subQuarters',
'y' => 'subYears',
], [
'd' => 'addDays',
'w' => 'addWeeks',
'm' => 'addMonths',
'q' => 'addQuarters',
'y' => 'addYears',
],
];
/** @var string $part */
foreach ($parts as $part) {
Log::debug(sprintf('Now parsing part "%s"', $part));
$part = trim($part);
// verify if correct
$pattern = '/[+-]\d+[wqmdy]/';
$res = preg_match($pattern, $part);
if (0 === $res || false === $res) {
Log::error(sprintf('Part "%s" does not match regular expression. Will be skipped.', $part));
continue;
}
$direction = 0 === strpos($part, '+') ? 1 : 0;
$period = $part[strlen($part) - 1];
$number = (int) substr($part, 1, -1);
if (!isset($functions[$direction][$period])) {
Log::error(sprintf('No method for direction %d and period "%s".', $direction, $period));
continue;
}
$func = $functions[$direction][$period];
Log::debug(sprintf('Will now do %s(%d) on %s', $func, $number, $today->format('Y-m-d')));
$today->$func($number);
Log::debug(sprintf('Resulting date is %s', $today->format('Y-m-d')));
}
return $today;
}
}

View File

@@ -584,4 +584,42 @@ class Steam
return $amount;
}
/**
* Get user's language.
*
* @return string
*/
public function getLanguage(): string // get preference
{
return app('preferences')->get('language', config('firefly.default_language', 'en_US'))->data;
}
/**
* Get user's locale.
*
* @return string
*/
public function getLocale(): string // get preference
{
/** @var string $language */
$locale = app('preferences')->get('locale', config('firefly.default_locale', 'equal'))->data;
if ('equal' === $locale) {
return $this->getLanguage();
}
return $locale;
}
/**
* @param string $locale
*
* @return array
*/
public function getLocaleArray(string $locale): array {
return [
sprintf('%s.utf8', $locale),
sprintf('%s.UTF-8', $locale),
];
}
}

View File

@@ -22,7 +22,9 @@ declare(strict_types=1);
namespace FireflyIII\Support;
use Carbon\Carbon;
use FireflyIII\Models\Telemetry as TelemetryModel;
use JsonException;
use Log;
/**
@@ -65,6 +67,25 @@ class Telemetry
}
}
/**
* @param string $key
* @param string $value
* @param int $days
*/
public function recurring(string $key, string $value, int $days): void
{
if (false === config('firefly.send_telemetry') || false === config('firefly.feature_flags.telemetry')) {
// hard stop if not allowed to do telemetry.
// do nothing!
return;
}
$cutoffDate = Carbon::today()->subDays($days);
if (!$this->hasRecentEntry('recurring', $key, $value, $cutoffDate)) {
$this->storeEntry('recurring', $key, $value);
}
}
/**
* String telemetry stores a string value as a telemetry entry. Values could include:
*
@@ -85,7 +106,6 @@ class Telemetry
}
Log::info(sprintf('Logged telemetry string "%s" with value "%s".', $name, $value));
// no storage backend yet, do nothing.
$this->storeEntry('string', $name, $value);
}
@@ -98,16 +118,49 @@ class Telemetry
*/
private function hasEntry(string $type, string $key, string $value): bool
{
try {
$jsonEncoded = json_encode($value, JSON_THROW_ON_ERROR, 512);
} catch (JsonException $e) {
Log::error(sprintf('JSON Exception encoding the following value: %s: %s', $value, $e->getMessage()));
$jsonEncoded = [];
}
return TelemetryModel
::where('type', $type)
->where('key', $key)
->where('value', json_encode($value, JSON_THROW_ON_ERROR, 512))
->where('value', $jsonEncoded)
->count() > 0;
}
/**
* @param string $type
* @param string $key
* @param string $value
* @param Carbon $date
*
* @return bool
*/
private function hasRecentEntry(string $type, string $key, string $value, Carbon $date): bool
{
try {
$jsonEncoded = json_encode($value, JSON_THROW_ON_ERROR, 512);
} catch (JsonException $e) {
Log::error(sprintf('JSON Exception encoding the following value: %s: %s', $value, $e->getMessage()));
$jsonEncoded = [];
}
return TelemetryModel
::where('type', $type)
->where('key', $key)
->where('created_at', '>=', $date->format('Y-m-d H:i:s'))
->where('value', $jsonEncoded)
->count() > 0;
}
/**
* Store new entry in DB.
*
* @param string $type
* @param string $name
* @param string $value
*/

View File

@@ -0,0 +1,81 @@
<?php
/**
* DeleteTransaction.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use Exception;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Services\Internal\Destroy\JournalDestroyService;
use FireflyIII\Services\Internal\Destroy\TransactionGroupDestroyService;
use Log;
/**
* Class DeleteTransaction.
*/
class DeleteTransaction implements ActionInterface
{
/**
* TriggerInterface constructor.
*
* @param RuleAction $action
*/
public function __construct(RuleAction $action)
{
}
/**
* Will delete transaction journal. Also the group if no other journals are in the group.
*
* @param TransactionJournal $journal
*
* @throws Exception
* @return bool
*/
public function act(TransactionJournal $journal): bool
{
$count = $journal->transactionGroup->transactionJournals()->count();
// destroy entire group.
if (1 === $count) {
Log::debug(
sprintf(
'RuleAction DeleteTransaction DELETED the entire transaction group of journal #%d ("%s").',
$journal->id, $journal->description
)
);
$service = app(TransactionGroupDestroyService::class);
$service->destroy($journal->transactionGroup);
return true;
}
Log::debug(sprintf('RuleAction DeleteTransaction DELETED transaction journal #%d ("%s").', $journal->id, $journal->description));
// trigger delete factory:
/** @var JournalDestroyService $service */
$service = app(JournalDestroyService::class);
$service->destroy($journal);
return true;
}
}

View File

@@ -73,7 +73,7 @@ class SetSourceAccount implements ActionInterface
$type = $journal->transactionType->type;
// if this is a transfer or a withdrawal, the new source account must be an asset account or a default account, and it MUST exist:
if ((TransactionType::WITHDRAWAL === $type || TransactionType::TRANSFER === $type) && !$this->findAssetAccount(AccountType::ASSET)) {
if ((TransactionType::WITHDRAWAL === $type || TransactionType::TRANSFER === $type) && !$this->findAssetAccount($type)) {
Log::error(
sprintf(
'Cannot change source account of journal #%d because no asset account with name "%s" exists.',
@@ -119,6 +119,10 @@ class SetSourceAccount implements ActionInterface
{
// switch on type:
$allowed = config(sprintf('firefly.expected_source_types.source.%s', $type));
$allowed = is_array($allowed) ? $allowed : [];
Log::debug(sprintf('Check config for expected_source_types.source.%s, result is', $type), $allowed);
$account = $this->repository->findByName($this->action->action_value, $allowed);
if (null === $account) {

View File

@@ -0,0 +1,166 @@
<?php
declare(strict_types=1);
namespace FireflyIII\TransactionRules\Actions;
use FireflyIII\Models\PiggyBank;
use FireflyIII\Models\RuleAction;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Models\TransactionType;
use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface;
use FireflyIII\User;
use Log;
/**
* Class UpdatePiggybank
*/
class UpdatePiggybank implements ActionInterface
{
/** @var RuleAction The rule action */
private $action;
/**
* TriggerInterface constructor.
*
* @param RuleAction $action
*/
public function __construct(RuleAction $action)
{
$this->action = $action;
}
/**
* @inheritDoc
*/
public function act(TransactionJournal $journal): bool
{
Log::debug(sprintf('Triggered rule action UpdatePiggybank on journal #%d', $journal->id));
if (TransactionType::TRANSFER !== $journal->transactionType->type) {
Log::info(sprintf('Journal #%d is a "%s" so skip this action.', $journal->id, $journal->transactionType->type));
return false;
}
$piggyBank = $this->findPiggybank($journal->user);
if (null === $piggyBank) {
Log::info(
sprintf(
'No piggy bank names "%s", cant execute action #%d of rule #%d ("%s")',
$this->action->value, $this->action->id, $this->action->rule_id, $this->action->rule->title,
)
);
return false;
}
Log::debug(sprintf('Found piggy bank #%d ("%s")', $piggyBank->id, $piggyBank->name));
/** @var Transaction $source */
$source = $journal->transactions()->where('amount', '<', 0)->first();
/** @var Transaction $destination */
$destination = $journal->transactions()->where('amount', '>', 0)->first();
if ((int) $source->account_id === (int) $piggyBank->account_id) {
Log::debug('Piggy bank account is linked to source, so remove amount.');
$this->removeAmount($journal, $piggyBank, $destination->amount);
return true;
}
if ((int) $destination->account_id === (int) $piggyBank->account_id) {
Log::debug('Piggy bank account is linked to source, so add amount.');
$this->addAmount($journal, $piggyBank, $destination->amount);
return true;
}
Log::info('Piggy bank is not linked to source or destination, so no action will be taken.');
return true;
}
/**
* @param TransactionJournal $journal
* @param PiggyBank $piggyBank
* @param string $amount
*/
private function addAmount(TransactionJournal $journal, PiggyBank $piggyBank, string $amount): void
{
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($journal->user);
// how much can we add to the piggy bank?
$toAdd = bcsub($piggyBank->targetamount, $repository->getCurrentAmount($piggyBank));
Log::debug(sprintf('Max amount to add to piggy bank is %s, amount is %s', $toAdd, $amount));
// update amount to fit:
$amount = -1 === bccomp($amount, $toAdd) ? $amount : $toAdd;
Log::debug(sprintf('Amount is now %s', $amount));
// if amount is zero, stop.
if (0 === bccomp('0', $amount)) {
Log::warning('Amount left is zero, stop.');
return;
}
// make sure we can add amount:
if (false === $repository->canAddAmount($piggyBank, $amount)) {
Log::warning(sprintf('Cannot add %s to piggy bank.', $amount));
return;
}
Log::debug(sprintf('Will now add %s to piggy bank.', $amount));
$repository->addAmount($piggyBank, $amount);
$repository->createEventWithJournal($piggyBank, app('steam')->positive($amount), $journal);
}
/**
* @param User $user
*
* @return PiggyBank|null
*/
private function findPiggybank(User $user): ?PiggyBank
{
return $user->piggyBanks()->where('piggy_banks.name', $this->action->action_value)->first();
}
/**
* @param TransactionJournal $journal
* @param PiggyBank $piggyBank
* @param string $amount
*/
private function removeAmount(TransactionJournal $journal, PiggyBank $piggyBank, string $amount): void
{
$repository = app(PiggyBankRepositoryInterface::class);
$repository->setUser($journal->user);
// how much can we remove from piggy bank?
$toRemove = $repository->getCurrentAmount($piggyBank);
Log::debug(sprintf('Amount is %s, max to remove is %s', $amount, $toRemove));
// if $amount is bigger than $toRemove, shrink it.
$amount = -1 === bccomp($amount, $toRemove) ? $amount : $toRemove;
Log::debug(sprintf('Amount is now %s', $amount));
// if amount is zero, stop.
if (0 === bccomp('0', $amount)) {
Log::warning('Amount left is zero, stop.');
return;
}
// make sure we can remove amount:
if (false === $repository->canRemoveAmount($piggyBank, $amount)) {
Log::warning(sprintf('Cannot remove %s from piggy bank.', $amount));
return;
}
Log::debug(sprintf('Will now remove %s from piggy bank.', $amount));
$repository->removeAmount($piggyBank, $amount);
$repository->createEventWithJournal($piggyBank, app('steam')->negative($amount), $journal);
}
}

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Triggers;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Log;
@@ -76,31 +75,6 @@ final class BudgetIs extends AbstractTrigger implements TriggerInterface
return true;
}
}
if (null === $budget) {
// perhaps transactions have this budget?
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$budget = $transaction->budgets()->first();
if (null !== $budget) {
$name = strtolower($budget->name);
if ($name === strtolower($this->triggerValue)) {
Log::debug(
sprintf(
'RuleTrigger BudgetIs for journal #%d (transaction #%d): "%s" is "%s", return true.',
$journal->id,
$transaction->id,
$name,
$this->triggerValue
)
);
return true;
}
}
}
}
Log::debug(sprintf('RuleTrigger BudgetIs for journal #%d: does not have budget "%s", return false.', $journal->id, $this->triggerValue));
return false;

View File

@@ -22,7 +22,6 @@ declare(strict_types=1);
namespace FireflyIII\TransactionRules\Triggers;
use FireflyIII\Models\Transaction;
use FireflyIII\Models\TransactionJournal;
use Log;
@@ -76,31 +75,6 @@ final class CategoryIs extends AbstractTrigger implements TriggerInterface
return true;
}
}
if (null === $category) {
// perhaps transactions have this category?
/** @var Transaction $transaction */
foreach ($journal->transactions as $transaction) {
$category = $transaction->categories()->first();
if (null !== $category) {
$name = strtolower($category->name);
if ($name === strtolower($this->triggerValue)) {
Log::debug(
sprintf(
'RuleTrigger CategoryIs for journal #%d (transaction #%d): "%s" is "%s", return true.',
$journal->id,
$transaction->id,
$name,
$this->triggerValue
)
);
return true;
}
}
}
}
Log::debug(sprintf('RuleTrigger CategoryIs for journal #%d: does not have category "%s", return false.', $journal->id, $this->triggerValue));
return false;

View File

@@ -0,0 +1,107 @@
<?php
/**
* DateAfter.php
* Copyright (c) 2019 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
declare(strict_types=1);
namespace FireflyIII\TransactionRules\Triggers;
use Carbon\Carbon;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Models\TransactionJournal;
use FireflyIII\Support\ParseDateString;
use Log;
/**
* Class DateAfter.
*/
final class DateAfter extends AbstractTrigger implements TriggerInterface
{
/**
* A trigger is said to "match anything", or match any given transaction,
* when the trigger value is very vague or has no restrictions. Easy examples
* are the "AmountMore"-trigger combined with an amount of 0: any given transaction
* has an amount of more than zero! Other examples are all the "Description"-triggers
* which have hard time handling empty trigger values such as "" or "*" (wild cards).
*
* If the user tries to create such a trigger, this method MUST return true so Firefly III
* can stop the storing / updating the trigger. If the trigger is in any way restrictive
* (even if it will still include 99.9% of the users transactions), this method MUST return
* false.
*
* @param mixed $value
*
* @return bool
*/
public static function willMatchEverything($value = null): bool
{
if (null !== $value) {
return false;
}
Log::error(sprintf('Cannot use %s with a null value.', self::class));
return true;
}
/**
* Returns true when category is X.
*
* @param TransactionJournal $journal
*
* @return bool
*/
public function triggered(TransactionJournal $journal): bool
{
/** @var Carbon $date */
$date = $journal->date;
Log::debug(sprintf('Found date on journal: %s', $date->format('Y-m-d')));
$dateParser = new ParseDateString();
try {
$ruleDate = $dateParser->parseDate($this->triggerValue);
} catch (FireflyException $e) {
Log::error('Cannot execute rule trigger.');
Log::error($e->getMessage());
return false;
}
if ($date->isAfter($ruleDate)) {
Log::debug(
sprintf(
'%s is after %s, so return true.',
$date->format('Y-m-d H:i:s'),
$ruleDate->format('Y-m-d H:i:s'),
)
);
return true;
}
Log::debug(
sprintf(
'%s is NOT after %s, so return true.',
$date->format('Y-m-d H:i:s'),
$ruleDate->format('Y-m-d H:i:s'),
)
);
return false;
}
}

Some files were not shown because too many files have changed in this diff Show More