mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-11-02 20:25:28 +00:00
Compare commits
204 Commits
5.4.0-alph
...
5.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ab38bfe4d2 | ||
|
|
dcb3f23078 | ||
|
|
c0f363cf86 | ||
|
|
46ebf3c07c | ||
|
|
ed7977a105 | ||
|
|
f093dbb30b | ||
|
|
620b69f234 | ||
|
|
a346b6ee29 | ||
|
|
ccb511936e | ||
|
|
9f98289952 | ||
|
|
e549b74c97 | ||
|
|
f73af50eb0 | ||
|
|
5f5346ed71 | ||
|
|
fc157b2944 | ||
|
|
cc1a537ecb | ||
|
|
1e5df66c62 | ||
|
|
e5ea82663e | ||
|
|
2d6e6f3ec9 | ||
|
|
a822c9f833 | ||
|
|
0b8b9cb280 | ||
|
|
29b8cf936e | ||
|
|
b288d6b0eb | ||
|
|
8a2d5b12c3 | ||
|
|
a7dc9e201f | ||
|
|
c686f16a93 | ||
|
|
58245d85b3 | ||
|
|
f564ef5195 | ||
|
|
18c24a7251 | ||
|
|
d97fd73ce5 | ||
|
|
1624abd9ed | ||
|
|
14f22009ed | ||
|
|
86cd3c0c38 | ||
|
|
855fe1235a | ||
|
|
198a0f7011 | ||
|
|
2b16d73e65 | ||
|
|
03f68426e5 | ||
|
|
229479b7ed | ||
|
|
0f742aa040 | ||
|
|
e5ac6a3a1d | ||
|
|
d05fb4472c | ||
|
|
5161971373 | ||
|
|
2a5841c631 | ||
|
|
766abd3336 | ||
|
|
b58cde5431 | ||
|
|
9964cf11a4 | ||
|
|
7f9c8fa133 | ||
|
|
10057f05a5 | ||
|
|
fc61b20f6d | ||
|
|
40e7f019dd | ||
|
|
2f32ddce6b | ||
|
|
459015c01e | ||
|
|
b669f96036 | ||
|
|
4524d1cfe3 | ||
|
|
651029e284 | ||
|
|
cbeb2675fd | ||
|
|
7a80caf26b | ||
|
|
f3eaf1dd4c | ||
|
|
af88e91a48 | ||
|
|
706cb47065 | ||
|
|
0d72aa9673 | ||
|
|
039e654ef1 | ||
|
|
1c3fe93d59 | ||
|
|
5ef45fd4a3 | ||
|
|
db700d6b80 | ||
|
|
7032bdc169 | ||
|
|
1dde09c738 | ||
|
|
a54ca0141a | ||
|
|
a553bfd142 | ||
|
|
ed01551ad4 | ||
|
|
91473332b4 | ||
|
|
890de05394 | ||
|
|
29445e4ffb | ||
|
|
098eac8bab | ||
|
|
e2ad5c60c6 | ||
|
|
34e2595a3d | ||
|
|
284222c2ee | ||
|
|
0b308cb5f2 | ||
|
|
22dc03f32c | ||
|
|
c2ff26b515 | ||
|
|
402351a6b7 | ||
|
|
562be457ec | ||
|
|
600e98cf47 | ||
|
|
bded065d42 | ||
|
|
a526007957 | ||
|
|
c0af49f336 | ||
|
|
ff6c0c26e0 | ||
|
|
a7af8d2195 | ||
|
|
f5871898bd | ||
|
|
df8f23cba3 | ||
|
|
d9bc23725b | ||
|
|
a68ab28f00 | ||
|
|
676c282f11 | ||
|
|
f99731e90b | ||
|
|
302ece8ffc | ||
|
|
47100a8e51 | ||
|
|
7e59d8cb75 | ||
|
|
7eaa00a5ad | ||
|
|
0713cc55b6 | ||
|
|
8329d68b22 | ||
|
|
3e05cf7306 | ||
|
|
8c116e1bc5 | ||
|
|
7d61986025 | ||
|
|
abff2a0a8f | ||
|
|
65499bb9ff | ||
|
|
6f219d1932 | ||
|
|
9d35048fb9 | ||
|
|
af58c30249 | ||
|
|
366744ed69 | ||
|
|
d809b77154 | ||
|
|
03cec69af3 | ||
|
|
9ae41b1645 | ||
|
|
b0f18a0419 | ||
|
|
5648a9197d | ||
|
|
f0fe8bf5c7 | ||
|
|
8a690afc46 | ||
|
|
2bb6c48278 | ||
|
|
6eb661df19 | ||
|
|
8e578e1058 | ||
|
|
4f7683b1fd | ||
|
|
656456b9d9 | ||
|
|
8c1daa9eed | ||
|
|
f146205208 | ||
|
|
dce3c333da | ||
|
|
b0d67f1637 | ||
|
|
bc913d0e9c | ||
|
|
415ef57458 | ||
|
|
278ddf27bd | ||
|
|
3566305c27 | ||
|
|
b41b03e8f0 | ||
|
|
e5642b59d7 | ||
|
|
e159de9a6a | ||
|
|
2a02e8a790 | ||
|
|
798c73394d | ||
|
|
7f48043505 | ||
|
|
82b49a9e51 | ||
|
|
56ea680e46 | ||
|
|
70f3d13626 | ||
|
|
093f34b7a8 | ||
|
|
63794cab07 | ||
|
|
ed17600f79 | ||
|
|
778af9f4e2 | ||
|
|
8c97754b64 | ||
|
|
2393344978 | ||
|
|
4c81a46af6 | ||
|
|
7df084dd3c | ||
|
|
08aa61a2bf | ||
|
|
a16ac479d5 | ||
|
|
483e7256f7 | ||
|
|
412b169e3a | ||
|
|
bfcd743efa | ||
|
|
f123c28540 | ||
|
|
56f9ce333c | ||
|
|
cd156d6991 | ||
|
|
c985683ee3 | ||
|
|
59ab0c9f0d | ||
|
|
a24b6711d1 | ||
|
|
7f7d7be646 | ||
|
|
2f2a02834b | ||
|
|
e2a3aa12a8 | ||
|
|
a4f70794a2 | ||
|
|
0ee3941b43 | ||
|
|
4e05ce4c35 | ||
|
|
c5fa48ca46 | ||
|
|
20e39b2e12 | ||
|
|
d28394c5d9 | ||
|
|
86fff1b61c | ||
|
|
71ef5abea5 | ||
|
|
6b83313ce8 | ||
|
|
2fe93656ee | ||
|
|
ec8003245f | ||
|
|
8f632cb57b | ||
|
|
d7b9129c33 | ||
|
|
82efe530ea | ||
|
|
b3805585a7 | ||
|
|
04b84fd0dc | ||
|
|
2ac405b2d0 | ||
|
|
eb8571a508 | ||
|
|
f7e75e9b8a | ||
|
|
0fd7dabbc1 | ||
|
|
9123454545 | ||
|
|
3141ec0406 | ||
|
|
4bf86500bd | ||
|
|
bee54146bf | ||
|
|
28698cc769 | ||
|
|
41f2339c8c | ||
|
|
ce34e097a2 | ||
|
|
fecc9f7659 | ||
|
|
139b3ffab4 | ||
|
|
febe60b3d1 | ||
|
|
fc519c41bc | ||
|
|
6e074d9b8b | ||
|
|
d89a4d8a54 | ||
|
|
a504617425 | ||
|
|
0c0fef6e84 | ||
|
|
1dfda62125 | ||
|
|
5e0c0e25f2 | ||
|
|
07220eb167 | ||
|
|
216a0a186c | ||
|
|
14df37712c | ||
|
|
ab5a146277 | ||
|
|
24a373abf4 | ||
|
|
ffca935ced | ||
|
|
d69934ca8f | ||
|
|
3081911eae |
@@ -9,6 +9,7 @@ parameters:
|
||||
- '#is not allowed to extend#'
|
||||
- '#is neither abstract nor final#'
|
||||
- '#Control structures using switch should not be used\.#'
|
||||
- '#has a nullable return type declaration#'
|
||||
paths:
|
||||
- ../app
|
||||
- ../database
|
||||
@@ -16,4 +17,4 @@ parameters:
|
||||
- ../bootstrap/app.php
|
||||
|
||||
# The level 8 is the highest level. original was 5
|
||||
level: 0
|
||||
level: 5
|
||||
|
||||
@@ -9,7 +9,6 @@ fi_FI
|
||||
fr_FR
|
||||
hu_HU
|
||||
it_IT
|
||||
lt_LT
|
||||
nb_NO
|
||||
nl_NL
|
||||
pl_PL
|
||||
|
||||
@@ -10,7 +10,6 @@ APP_DEBUG=false
|
||||
SITE_OWNER=mail@example.com
|
||||
|
||||
# The encryption key for your sessions. Keep this very secure.
|
||||
# If you generate a new one all existing attachments must be considered LOST.
|
||||
# Change it to a string of exactly 32 chars or use something like `php artisan key:generate` to generate it.
|
||||
# If you use Docker or similar, you can set this variable from a file by using APP_KEY_FILE
|
||||
APP_KEY=SomeRandomStringOf32CharsExactly
|
||||
@@ -143,6 +142,7 @@ SPARKPOST_SECRET=
|
||||
# Firefly III can send you the following messages
|
||||
SEND_REGISTRATION_MAIL=true
|
||||
SEND_ERROR_MESSAGE=true
|
||||
SEND_LOGIN_NEW_IP_WARNING=true
|
||||
|
||||
# These messages contain (sensitive) transaction information:
|
||||
SEND_REPORT_JOURNALS=true
|
||||
|
||||
3
.gitattributes
vendored
3
.gitattributes
vendored
@@ -3,3 +3,6 @@
|
||||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
CHANGELOG.md export-ignore
|
||||
/tests export-ignore
|
||||
/phpunit.xml export-ignore
|
||||
/.ci export-ignore
|
||||
|
||||
7
.github/.mergify.yml
vendored
Normal file
7
.github/.mergify.yml
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
pull_request_rules:
|
||||
- name: PR on main is never approved.
|
||||
conditions:
|
||||
- base=main
|
||||
actions:
|
||||
close:
|
||||
message: Please reopen this PR on the `develop` branch. Thank you.
|
||||
1
.github/funding.yml
vendored
1
.github/funding.yml
vendored
@@ -2,4 +2,3 @@
|
||||
|
||||
github: jc5
|
||||
patreon: JC5
|
||||
custom: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=L62W7DVD5ETPC&source=url
|
||||
|
||||
@@ -145,6 +145,9 @@ class AvailableBudgetController extends Controller
|
||||
public function store(AvailableBudgetRequest $request): JsonResponse
|
||||
{
|
||||
$data = $request->getAll();
|
||||
$data['start']->startOfDay();
|
||||
$data['end']->endOfDay();
|
||||
|
||||
/** @var TransactionCurrencyFactory $factory */
|
||||
$factory = app(TransactionCurrencyFactory::class);
|
||||
$currency = $factory->find($data['currency_id'], $data['currency_code']);
|
||||
|
||||
@@ -54,6 +54,15 @@ abstract class Controller extends BaseController
|
||||
{
|
||||
// get global parameters
|
||||
$this->parameters = $this->getParameters();
|
||||
$this->middleware(
|
||||
function ($request, $next) {
|
||||
if (auth()->check()) {
|
||||
$language = app('steam')->getLanguage();
|
||||
app()->setLocale($language);
|
||||
}
|
||||
return $next($request);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,21 +28,19 @@ use FireflyIII\Api\V1\Requests\RuleTestRequest;
|
||||
use FireflyIII\Api\V1\Requests\RuleTriggerRequest;
|
||||
use FireflyIII\Api\V1\Requests\RuleUpdateRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use FireflyIII\Transformers\RuleTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\Support\Collection;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RuleController
|
||||
@@ -215,29 +213,37 @@ class RuleController extends Controller
|
||||
* @param RuleTestRequest $request
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function testRule(RuleTestRequest $request, Rule $rule): JsonResponse
|
||||
{
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
$parameters = $request->getTestParameters();
|
||||
/** @var Rule $rule */
|
||||
Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title));
|
||||
/** @var TransactionMatcher $matcher */
|
||||
$matcher = app(TransactionMatcher::class);
|
||||
// set all parameters:
|
||||
$matcher->setRule($rule);
|
||||
$matcher->setStartDate($parameters['start_date']);
|
||||
$matcher->setEndDate($parameters['end_date']);
|
||||
$matcher->setSearchLimit($parameters['search_limit']);
|
||||
$matcher->setTriggeredLimit($parameters['trigger_limit']);
|
||||
$matcher->setAccounts($parameters['accounts']);
|
||||
|
||||
$matchingTransactions = $matcher->findTransactionsByRule();
|
||||
$count = count($matchingTransactions);
|
||||
$transactions = array_slice($matchingTransactions, ($parameters['page'] - 1) * $pageSize, $pageSize);
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $this->parameters->get('page'));
|
||||
/** @var RuleEngineInterface $ruleEngine */
|
||||
$ruleEngine = app(RuleEngineInterface::class);
|
||||
$ruleEngine->setRules(new Collection([$rule]));
|
||||
|
||||
|
||||
// overrule the rule(s) if necessary.
|
||||
if (array_key_exists('start', $parameters) && null !== $parameters['start']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]);
|
||||
}
|
||||
|
||||
if (array_key_exists('end', $parameters) && null !== $parameters['end']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]);
|
||||
}
|
||||
if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) {
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]);
|
||||
}
|
||||
|
||||
// file the rule(s)
|
||||
$transactions = $ruleEngine->find();
|
||||
$count = $transactions->count();
|
||||
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.rules.test', [$rule->id]) . $this->buildParams());
|
||||
|
||||
// resulting list is presented as JSON thing.
|
||||
@@ -246,7 +252,7 @@ class RuleController extends Controller
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($matchingTransactions, $transformer, 'transactions');
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
@@ -265,28 +271,27 @@ class RuleController extends Controller
|
||||
// Get parameters specified by the user
|
||||
$parameters = $request->getTriggerParameters();
|
||||
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser(auth()->user());
|
||||
/** @var RuleEngineInterface $ruleEngine */
|
||||
$ruleEngine = app(RuleEngineInterface::class);
|
||||
$ruleEngine->setRules(new Collection([$rule]));
|
||||
|
||||
$rules = [$rule->id];
|
||||
|
||||
$ruleEngine->setRulesToApply($rules);
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts($parameters['accounts']);
|
||||
$collector->setRange($parameters['start_date'], $parameters['end_date']);
|
||||
$journals = $collector->getExtractedJournals();
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
Log::debug('Start of new journal.');
|
||||
$ruleEngine->processJournalArray($journal);
|
||||
Log::debug('Done with all rules for this group + done with journal.');
|
||||
// overrule the rule(s) if necessary.
|
||||
if (array_key_exists('start', $parameters) && null !== $parameters['start']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]);
|
||||
}
|
||||
|
||||
if (array_key_exists('end', $parameters) && null !== $parameters['end']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]);
|
||||
}
|
||||
if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) {
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]);
|
||||
}
|
||||
|
||||
// file the rule(s)
|
||||
$ruleEngine->fire();
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,10 @@ use FireflyIII\Api\V1\Requests\RuleGroupRequest;
|
||||
use FireflyIII\Api\V1\Requests\RuleGroupTestRequest;
|
||||
use FireflyIII\Api\V1\Requests\RuleGroupTriggerRequest;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use FireflyIII\Transformers\RuleGroupTransformer;
|
||||
use FireflyIII\Transformers\RuleTransformer;
|
||||
use FireflyIII\Transformers\TransactionGroupTransformer;
|
||||
@@ -45,7 +42,6 @@ use Illuminate\Support\Collection;
|
||||
use League\Fractal\Pagination\IlluminatePaginatorAdapter;
|
||||
use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RuleGroupController
|
||||
@@ -248,54 +244,51 @@ class RuleGroupController extends Controller
|
||||
* @param RuleGroupTestRequest $request
|
||||
* @param RuleGroup $group
|
||||
*
|
||||
* @return JsonResponse
|
||||
* @throws FireflyException
|
||||
*
|
||||
* @return JsonResponse
|
||||
*/
|
||||
public function testGroup(RuleGroupTestRequest $request, RuleGroup $group): JsonResponse
|
||||
{
|
||||
$pageSize = (int) app('preferences')->getForUser(auth()->user(), 'listPageSize', 50)->data;
|
||||
Log::debug('Now in testGroup()');
|
||||
/** @var Collection $rules */
|
||||
$rules = $this->ruleGroupRepository->getActiveRules($group);
|
||||
if (0 === $rules->count()) {
|
||||
throw new FireflyException('200023: No rules in this rule group.');
|
||||
}
|
||||
$parameters = $request->getTestParameters();
|
||||
$matchingTransactions = [];
|
||||
$parameters = $request->getTestParameters();
|
||||
|
||||
Log::debug(sprintf('Going to test %d rules', $rules->count()));
|
||||
/** @var Rule $rule */
|
||||
foreach ($rules as $rule) {
|
||||
Log::debug(sprintf('Now testing rule #%d, "%s"', $rule->id, $rule->title));
|
||||
/** @var TransactionMatcher $matcher */
|
||||
$matcher = app(TransactionMatcher::class);
|
||||
// set all parameters:
|
||||
$matcher->setRule($rule);
|
||||
$matcher->setStartDate($parameters['start_date']);
|
||||
$matcher->setEndDate($parameters['end_date']);
|
||||
$matcher->setSearchLimit($parameters['search_limit']);
|
||||
$matcher->setTriggeredLimit($parameters['trigger_limit']);
|
||||
$matcher->setAccounts($parameters['accounts']);
|
||||
/** @var RuleEngineInterface $ruleEngine */
|
||||
$ruleEngine = app(RuleEngineInterface::class);
|
||||
$ruleEngine->setRules($rules);
|
||||
|
||||
$result = $matcher->findTransactionsByRule();
|
||||
/** @noinspection AdditionOperationOnArraysInspection */
|
||||
$matchingTransactions = $result + $matchingTransactions;
|
||||
// overrule the rule(s) if necessary.
|
||||
if (array_key_exists('start', $parameters) && null !== $parameters['start']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]);
|
||||
}
|
||||
|
||||
// make paginator out of results.
|
||||
$count = count($matchingTransactions);
|
||||
$transactions = array_slice($matchingTransactions, ($parameters['page'] - 1) * $pageSize, $pageSize);
|
||||
if (array_key_exists('end', $parameters) && null !== $parameters['end']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]);
|
||||
}
|
||||
if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) {
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]);
|
||||
}
|
||||
|
||||
// make paginator:
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, $pageSize, $parameters['page']);
|
||||
// file the rule(s)
|
||||
$transactions = $ruleEngine->find();
|
||||
$count = $transactions->count();
|
||||
|
||||
$paginator = new LengthAwarePaginator($transactions, $count, 31337, $this->parameters->get('page'));
|
||||
$paginator->setPath(route('api.v1.rule_groups.test', [$group->id]) . $this->buildParams());
|
||||
|
||||
$manager = $this->getManager();
|
||||
// resulting list is presented as JSON thing.
|
||||
$manager = $this->getManager();
|
||||
/** @var TransactionGroupTransformer $transformer */
|
||||
$transformer = app(TransactionGroupTransformer::class);
|
||||
$transformer->setParameters($this->parameters);
|
||||
|
||||
$resource = new FractalCollection($matchingTransactions, $transformer, 'transactions');
|
||||
$resource = new FractalCollection($transactions, $transformer, 'transactions');
|
||||
$resource->setPaginator(new IlluminatePaginatorAdapter($paginator));
|
||||
|
||||
return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json');
|
||||
@@ -307,40 +300,40 @@ class RuleGroupController extends Controller
|
||||
* @param RuleGroupTriggerRequest $request
|
||||
* @param RuleGroup $group
|
||||
*
|
||||
* @throws Exception
|
||||
* @return JsonResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function triggerGroup(RuleGroupTriggerRequest $request, RuleGroup $group): JsonResponse
|
||||
{
|
||||
/** @var Collection $rules */
|
||||
$rules = $this->ruleGroupRepository->getActiveRules($group);
|
||||
if (0 === $rules->count()) {
|
||||
throw new FireflyException('200023: No rules in this rule group.');
|
||||
}
|
||||
|
||||
// Get parameters specified by the user
|
||||
$parameters = $request->getTriggerParameters();
|
||||
|
||||
/** @var Collection $collection */
|
||||
$collection = $this->ruleGroupRepository->getActiveRules($group);
|
||||
$rules = [];
|
||||
/** @var Rule $item */
|
||||
foreach ($collection as $item) {
|
||||
$rules[] = $item->id;
|
||||
/** @var RuleEngineInterface $ruleEngine */
|
||||
$ruleEngine = app(RuleEngineInterface::class);
|
||||
$ruleEngine->setRules($rules);
|
||||
|
||||
// overrule the rule(s) if necessary.
|
||||
if (array_key_exists('start', $parameters) && null !== $parameters['start']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_after', 'value' => $parameters['start']->format('Y-m-d')]);
|
||||
}
|
||||
|
||||
// start looping.
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser(auth()->user());
|
||||
$ruleEngine->setRulesToApply($rules);
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts($parameters['accounts']);
|
||||
$collector->setRange($parameters['start_date'], $parameters['end_date']);
|
||||
$journals = $collector->getExtractedJournals();
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
Log::debug('Start of new journal.');
|
||||
$ruleEngine->processJournalArray($journal);
|
||||
Log::debug('Done with all rules for this group + done with journal.');
|
||||
if (array_key_exists('end', $parameters) && null !== $parameters['end']) {
|
||||
// add a range:
|
||||
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $parameters['end']->format('Y-m-d')]);
|
||||
}
|
||||
if (array_key_exists('accounts', $parameters) && '' !== $parameters['accounts']) {
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => $parameters['accounts']]);
|
||||
}
|
||||
|
||||
// file the rule(s)
|
||||
$ruleEngine->fire();
|
||||
|
||||
return response()->json([], 204);
|
||||
}
|
||||
|
||||
@@ -328,7 +328,7 @@ class SummaryController extends Controller
|
||||
private function getLeftToSpendInfo(Carbon $start, Carbon $end): array
|
||||
{
|
||||
$return = [];
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
$available = $this->abRepository->getAvailableBudgetWithCurrency($start, $end);
|
||||
$budgets = $this->budgetRepository->getActiveBudgets();
|
||||
$spent = $this->opsRepository->sumExpenses($start, $end, null, $budgets);
|
||||
|
||||
@@ -50,7 +50,6 @@ use League\Fractal\Resource\Collection as FractalCollection;
|
||||
use League\Fractal\Resource\Item;
|
||||
use Log;
|
||||
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
|
||||
|
||||
/**
|
||||
* Class TransactionController
|
||||
*/
|
||||
@@ -59,9 +58,7 @@ class TransactionController extends Controller
|
||||
use TransactionFilter;
|
||||
|
||||
private TransactionGroupRepositoryInterface $groupRepository;
|
||||
|
||||
private JournalAPIRepositoryInterface $journalAPIRepository;
|
||||
|
||||
private JournalRepositoryInterface $repository;
|
||||
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ use Log;
|
||||
class RuleGroupTestRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -57,12 +58,9 @@ class RuleGroupTestRequest extends FormRequest
|
||||
public function getTestParameters(): array
|
||||
{
|
||||
return [
|
||||
'page' => $this->getPage(),
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
'search_limit' => $this->getSearchLimit(),
|
||||
'trigger_limit' => $this->getTriggerLimit(),
|
||||
'accounts' => $this->getAccounts(),
|
||||
'start' => $this->getDate('start'),
|
||||
'end' => $this->getDate('end'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -71,31 +69,20 @@ class RuleGroupTestRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
private function getAccounts(): Collection
|
||||
private function getAccounts(): string
|
||||
{
|
||||
$accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts'));
|
||||
$accounts = new Collection;
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
foreach ($accountList as $accountId) {
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int) $accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
return (string) $this->query('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,39 +98,4 @@ class RuleGroupTestRequest extends FormRequest
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function getPage(): int
|
||||
{
|
||||
return 0 === (int) $this->query('page') ? 1 : (int) $this->query('page');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function getSearchLimit(): int
|
||||
{
|
||||
return 0 === (int) $this->query('search_limit') ? (int) config('firefly.test-triggers.limit') : (int) $this->query('search_limit');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function getTriggerLimit(): int
|
||||
{
|
||||
return 0 === (int) $this->query('triggered_limit') ? (int) config('firefly.test-triggers.range') : (int) $this->query('triggered_limit');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validAccount(?Account $account): bool
|
||||
{
|
||||
return null !== $account && AccountType::ASSET === $account->accountType->type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ use Log;
|
||||
class RuleGroupTriggerRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -57,9 +58,9 @@ class RuleGroupTriggerRequest extends FormRequest
|
||||
public function getTriggerParameters(): array
|
||||
{
|
||||
return [
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
'start' => $this->getDate('start'),
|
||||
'end' => $this->getDate('end'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -69,33 +70,17 @@ class RuleGroupTriggerRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date|after:start_date',
|
||||
'start' => 'date',
|
||||
'end' => 'date|after:start',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
* @return string
|
||||
*/
|
||||
private function getAccounts(): Collection
|
||||
private function getAccounts(): string
|
||||
{
|
||||
$accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts'));
|
||||
$accounts = new Collection;
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
foreach ($accountList as $accountId) {
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int) $accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
return (string) $this->query('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,19 +91,7 @@ class RuleGroupTriggerRequest extends FormRequest
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
/** @var Carbon $result */
|
||||
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validAccount(?Account $account): bool
|
||||
{
|
||||
return null !== $account && AccountType::ASSET === $account->accountType->type;
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\Support\Request\GetRuleConfiguration;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Validator;
|
||||
use function is_array;
|
||||
@@ -35,7 +36,8 @@ use function is_array;
|
||||
*/
|
||||
class RuleStoreRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
use ConvertsDataTypes, GetRuleConfiguration;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -88,11 +90,11 @@ class RuleStoreRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
||||
$validTriggers = $this->getTriggers();
|
||||
$validActions = array_keys(config('firefly.rule-actions'));
|
||||
|
||||
// some triggers and actions require text:
|
||||
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
|
||||
$contextTriggers = implode(',', $this->getTriggersWithContext());
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
|
||||
return [
|
||||
|
||||
@@ -26,13 +26,8 @@ namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RuleTestRequest
|
||||
@@ -40,6 +35,7 @@ use Log;
|
||||
class RuleTestRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -57,12 +53,11 @@ class RuleTestRequest extends FormRequest
|
||||
public function getTestParameters(): array
|
||||
{
|
||||
return [
|
||||
'page' => $this->getPage(),
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
'search_limit' => $this->getSearchLimit(),
|
||||
'trigger_limit' => $this->getTriggerLimit(),
|
||||
'accounts' => $this->getAccounts(),
|
||||
'page' => $this->getPage(),
|
||||
'start' => $this->getDate('start'),
|
||||
'end' => $this->getDate('end'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
@@ -71,31 +66,20 @@ class RuleTestRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [];
|
||||
return [
|
||||
'start' => 'date',
|
||||
'end' => 'date|after:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
* @return string
|
||||
*/
|
||||
private function getAccounts(): Collection
|
||||
private function getAccounts(): string
|
||||
{
|
||||
$accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts'));
|
||||
$accounts = new Collection;
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
foreach ($accountList as $accountId) {
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int) $accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
return (string) $this->query('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -120,30 +104,4 @@ class RuleTestRequest extends FormRequest
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function getSearchLimit(): int
|
||||
{
|
||||
return 0 === (int) $this->query('search_limit') ? (int) config('firefly.test-triggers.limit') : (int) $this->query('search_limit');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
private function getTriggerLimit(): int
|
||||
{
|
||||
return 0 === (int) $this->query('triggered_limit') ? (int) config('firefly.test-triggers.range') : (int) $this->query('triggered_limit');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validAccount(?Account $account): bool
|
||||
{
|
||||
return null !== $account && AccountType::ASSET === $account->accountType->type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -26,13 +26,8 @@ namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class RuleTriggerRequest
|
||||
@@ -40,6 +35,7 @@ use Log;
|
||||
class RuleTriggerRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -57,9 +53,9 @@ class RuleTriggerRequest extends FormRequest
|
||||
public function getTriggerParameters(): array
|
||||
{
|
||||
return [
|
||||
'start_date' => $this->getDate('start_date'),
|
||||
'end_date' => $this->getDate('end_date'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
'start' => $this->getDate('start'),
|
||||
'end' => $this->getDate('end'),
|
||||
'accounts' => $this->getAccounts(),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -69,33 +65,19 @@ class RuleTriggerRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'start_date' => 'required|date',
|
||||
'end_date' => 'required|date|after:start_date',
|
||||
'start' => 'date',
|
||||
'end' => 'date|after:start',
|
||||
'accounts' => '',
|
||||
'accounts.*' => 'exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
* @return string
|
||||
*/
|
||||
private function getAccounts(): Collection
|
||||
private function getAccounts(): string
|
||||
{
|
||||
$accountList = '' === (string) $this->query('accounts') ? [] : explode(',', $this->query('accounts'));
|
||||
$accounts = new Collection;
|
||||
|
||||
/** @var AccountRepositoryInterface $accountRepository */
|
||||
$accountRepository = app(AccountRepositoryInterface::class);
|
||||
|
||||
foreach ($accountList as $accountId) {
|
||||
Log::debug(sprintf('Searching for asset account with id "%s"', $accountId));
|
||||
$account = $accountRepository->findNull((int) $accountId);
|
||||
if ($this->validAccount($account)) {
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
Log::debug(sprintf('Found account #%d ("%s") and its an asset account', $account->id, $account->name));
|
||||
$accounts->push($account);
|
||||
}
|
||||
}
|
||||
|
||||
return $accounts;
|
||||
return (string) $this->query('accounts');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -105,20 +87,7 @@ class RuleTriggerRequest extends FormRequest
|
||||
*/
|
||||
private function getDate(string $field): ?Carbon
|
||||
{
|
||||
/** @var Carbon $result */
|
||||
$result = null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Account|null $account
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function validAccount(?Account $account): bool
|
||||
{
|
||||
return null !== $account && AccountType::ASSET === $account->accountType->type;
|
||||
return null === $this->query($field) ? null : Carbon::createFromFormat('Y-m-d', $this->query($field));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ namespace FireflyIII\Api\V1\Requests;
|
||||
|
||||
use FireflyIII\Rules\IsBoolean;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\Support\Request\GetRuleConfiguration;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Validator;
|
||||
use function is_array;
|
||||
@@ -35,7 +36,7 @@ use function is_array;
|
||||
*/
|
||||
class RuleUpdateRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
use ConvertsDataTypes, GetRuleConfiguration;
|
||||
/**
|
||||
* Authorize logged in users.
|
||||
*
|
||||
@@ -88,12 +89,12 @@ class RuleUpdateRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
||||
$validTriggers = $this->getTriggers();
|
||||
$validActions = array_keys(config('firefly.rule-actions'));
|
||||
$rule = $this->route()->parameter('rule');
|
||||
|
||||
// some triggers and actions require text:
|
||||
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
|
||||
$contextTriggers = implode(',', $this->getTriggersWithContext());
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
|
||||
return [
|
||||
|
||||
@@ -42,23 +42,12 @@ class TransactionUpdateRequest extends FormRequest
|
||||
{
|
||||
use TransactionValidation, GroupValidation, ConvertsDataTypes;
|
||||
|
||||
/** @var array Array values. */
|
||||
private $arrayFields;
|
||||
|
||||
/** @var array Boolean values. */
|
||||
private $booleanFields;
|
||||
|
||||
/** @var array Fields that contain date values. */
|
||||
private $dateFields;
|
||||
|
||||
/** @var array Fields that contain integer values. */
|
||||
private $integerFields;
|
||||
|
||||
/** @var array Fields that contain string values. */
|
||||
private $stringFields;
|
||||
|
||||
/** @var array Fields that contain text (with newlines) */
|
||||
private $textareaFields;
|
||||
private array $arrayFields;
|
||||
private array $booleanFields;
|
||||
private array $dateFields;
|
||||
private array $integerFields;
|
||||
private array $stringFields;
|
||||
private array $textareaFields;
|
||||
|
||||
|
||||
/**
|
||||
@@ -177,12 +166,12 @@ class TransactionUpdateRequest extends FormRequest
|
||||
// currency info
|
||||
'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'min:3|max:3|exists:transaction_currencies,code',
|
||||
'transactions.*.foreign_currency_id' => 'nullable|numeric|exists:transaction_currencies,id',
|
||||
'transactions.*.foreign_currency_code' => 'nullable|min:3|max:3|exists:transaction_currencies,code',
|
||||
|
||||
// amount
|
||||
'transactions.*.amount' => 'numeric|gt:0|max:100000000000',
|
||||
'transactions.*.foreign_amount' => 'numeric|gte:0',
|
||||
'transactions.*.foreign_amount' => 'nullable|numeric|gte:0',
|
||||
|
||||
// description
|
||||
'transactions.*.description' => 'nullable|between:1,1000',
|
||||
|
||||
@@ -55,7 +55,7 @@ class CreateDatabase extends Command
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
if ('mysql' !== env('DB_CONNECTION')) {
|
||||
if ('mysql' !== env('DB_CONNECTION', 'mysql')) {
|
||||
$this->info(sprintf('CreateDB does not apply to "%s", skipped.', env('DB_CONNECTION')));
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -230,7 +230,7 @@ class ExportData extends Command
|
||||
return $date;
|
||||
}
|
||||
if ('end' === $field) {
|
||||
$date = new Carbon;
|
||||
$date = today(config('app.timezone'));
|
||||
$date->endOfDay();
|
||||
|
||||
return $date;
|
||||
|
||||
@@ -27,7 +27,6 @@ namespace FireflyIII\Console\Commands\Tools;
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Console\Commands\VerifiesAccessToken;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
@@ -35,7 +34,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Journal\JournalRepositoryInterface;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
@@ -58,7 +57,7 @@ class ApplyRules extends Command
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature
|
||||
protected $signature
|
||||
= 'firefly-iii:apply-rules
|
||||
{--user=1 : The user ID.}
|
||||
{--token= : The user\'s access token.}
|
||||
@@ -68,44 +67,33 @@ class ApplyRules extends Command
|
||||
{--all_rules : If set, will overrule both settings and simply apply ALL of your rules.}
|
||||
{--start_date= : The date of the earliest transaction to be included (inclusive). If omitted, will be your very first transaction ever. Format: YYYY-MM-DD}
|
||||
{--end_date= : The date of the latest transaction to be included (inclusive). If omitted, will be your latest transaction ever. Format: YYYY-MM-DD}';
|
||||
/** @var array */
|
||||
private $acceptedAccounts;
|
||||
/** @var Collection */
|
||||
private $accounts;
|
||||
/** @var bool */
|
||||
private $allRules;
|
||||
/** @var Carbon */
|
||||
private $endDate;
|
||||
/** @var Collection */
|
||||
private $groups;
|
||||
/** @var RuleGroupRepositoryInterface */
|
||||
private $ruleGroupRepository;
|
||||
/** @var array */
|
||||
private $ruleGroupSelection;
|
||||
/** @var RuleRepositoryInterface */
|
||||
private $ruleRepository;
|
||||
/** @var array */
|
||||
private $ruleSelection;
|
||||
/** @var Carbon */
|
||||
private $startDate;
|
||||
private array $acceptedAccounts;
|
||||
private Collection $accounts;
|
||||
private bool $allRules;
|
||||
private Carbon $endDate;
|
||||
private Collection $groups;
|
||||
private RuleGroupRepositoryInterface $ruleGroupRepository;
|
||||
private array $ruleGroupSelection;
|
||||
private RuleRepositoryInterface $ruleRepository;
|
||||
private array $ruleSelection;
|
||||
private Carbon $startDate;
|
||||
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return int
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$start = microtime(true);
|
||||
$this->stupidLaravel();
|
||||
// @codeCoverageIgnoreStart
|
||||
if (!$this->verifyAccessToken()) {
|
||||
$this->error('Invalid access token.');
|
||||
|
||||
return 1;
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
// set user:
|
||||
$this->ruleRepository->setUser($this->getUser());
|
||||
@@ -124,7 +112,7 @@ class ApplyRules extends Command
|
||||
|
||||
// loop all groups and rules and indicate if they're included:
|
||||
$rulesToApply = $this->getRulesToApply();
|
||||
$count = count($rulesToApply);
|
||||
$count = $rulesToApply->count();
|
||||
if (0 === $count) {
|
||||
$this->error('No rules or rule groups have been included.');
|
||||
$this->warn('Make a selection using:');
|
||||
@@ -136,48 +124,45 @@ class ApplyRules extends Command
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setUser($this->getUser());
|
||||
$collector->setAccounts($this->accounts);
|
||||
$collector->setRange($this->startDate, $this->endDate);
|
||||
$journals = $collector->getExtractedJournals();
|
||||
// create new rule engine:
|
||||
/** @var RuleEngineInterface $ruleEngine */
|
||||
$ruleEngine = app(RuleEngineInterface::class);
|
||||
$ruleEngine->setRules($rulesToApply);
|
||||
$ruleEngine->setUser($this->getUser());
|
||||
|
||||
// add the accounts as filter:
|
||||
$accounts = [];
|
||||
foreach($this->accounts as $account) {
|
||||
$accounts[] = $account->id;
|
||||
}
|
||||
$list = implode(',', $accounts);
|
||||
$ruleEngine->addOperator(['type' => 'account_id', 'value' => $list]);
|
||||
|
||||
// add the date as a filter:
|
||||
$ruleEngine->addOperator(['type' => 'date_after', 'value' => $this->startDate->format('Y-m-d')]);
|
||||
$ruleEngine->addOperator(['type' => 'date_before', 'value' => $this->endDate->format('Y-m-d')]);
|
||||
|
||||
// start running rules.
|
||||
$this->line(sprintf('Will apply %d rule(s) to %d transaction(s).', $count, count($journals)));
|
||||
$this->line(sprintf('Will apply %d rule(s) to your transaction(s).', $count));
|
||||
|
||||
// start looping.
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser($this->getUser());
|
||||
$ruleEngine->setRulesToApply($rulesToApply);
|
||||
|
||||
// for this call, the rule engine only includes "store" rules:
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
|
||||
|
||||
$bar = $this->output->createProgressBar(count($journals));
|
||||
Log::debug(sprintf('Now looping %d transactions.', count($journals)));
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
Log::debug('Start of new journal.');
|
||||
$ruleEngine->processJournalArray($journal);
|
||||
Log::debug('Done with all rules for this group + done with journal.');
|
||||
/** @noinspection DisconnectedForeachInstructionInspection */
|
||||
$bar->advance();
|
||||
}
|
||||
$this->line('');
|
||||
$this->line('Done!');
|
||||
// file the rule(s)
|
||||
$ruleEngine->fire();
|
||||
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
|
||||
$this->line('');
|
||||
$end = round(microtime(true) - $start, 2);
|
||||
$this->line(sprintf('Done in %s seconds!', $end));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* @return Collection
|
||||
*/
|
||||
private function getRulesToApply(): array
|
||||
private function getRulesToApply(): Collection
|
||||
{
|
||||
$rulesToApply = [];
|
||||
$rulesToApply = new Collection;
|
||||
/** @var RuleGroup $group */
|
||||
foreach ($this->groups as $group) {
|
||||
$rules = $this->ruleGroupRepository->getActiveStoreRules($group);
|
||||
@@ -187,7 +172,7 @@ class ApplyRules extends Command
|
||||
$test = $this->includeRule($rule, $group);
|
||||
if (true === $test) {
|
||||
Log::debug(sprintf('Will include rule #%d "%s"', $rule->id, $rule->title));
|
||||
$rulesToApply[] = $rule->id;
|
||||
$rulesToApply->push($rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,8 +220,8 @@ class ApplyRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function verifyInput(): bool
|
||||
{
|
||||
@@ -258,8 +243,8 @@ class ApplyRules extends Command
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FireflyException
|
||||
* @return bool
|
||||
* @throws FireflyException
|
||||
*/
|
||||
private function verifyInputAccounts(): bool
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace FireflyIII\Console\Commands;
|
||||
use FireflyIII\Support\System\GeneratesInstallationId;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
|
||||
/**
|
||||
* Class UpgradeFireflyInstructions.
|
||||
@@ -56,10 +57,10 @@ class UpgradeFireflyInstructions extends Command
|
||||
public function handle(): int
|
||||
{
|
||||
$this->generateInstallationId();
|
||||
if ('update' === (string) $this->argument('task')) {
|
||||
if ('update' === (string)$this->argument('task')) {
|
||||
$this->updateInstructions();
|
||||
}
|
||||
if ('install' === (string) $this->argument('task')) {
|
||||
if ('install' === (string)$this->argument('task')) {
|
||||
$this->installInstructions();
|
||||
}
|
||||
|
||||
@@ -70,7 +71,11 @@ class UpgradeFireflyInstructions extends Command
|
||||
app('telemetry')->feature('system.database.driver', env('DB_CONNECTION', '(unknown)'));
|
||||
app('telemetry')->feature('system.os.is_docker', $isDocker);
|
||||
app('telemetry')->feature('system.command.executed', $this->signature);
|
||||
app('telemetry')->feature('system.users.count', (string) User::count());
|
||||
try {
|
||||
app('telemetry')->feature('system.users.count', (string)User::count());
|
||||
} catch (QueryException $e) {
|
||||
// ignore error.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -108,8 +113,8 @@ class UpgradeFireflyInstructions extends Command
|
||||
{
|
||||
/** @var string $version */
|
||||
$version = config('firefly.version');
|
||||
$config = config('upgrade.text.install');
|
||||
$text = '';
|
||||
$config = config('upgrade.text.install');
|
||||
$text = '';
|
||||
foreach (array_keys($config) as $compare) {
|
||||
// if string starts with:
|
||||
if (0 === strpos($version, $compare)) {
|
||||
@@ -156,8 +161,8 @@ class UpgradeFireflyInstructions extends Command
|
||||
{
|
||||
/** @var string $version */
|
||||
$version = config('firefly.version');
|
||||
$config = config('upgrade.text.upgrade');
|
||||
$text = '';
|
||||
$config = config('upgrade.text.upgrade');
|
||||
$text = '';
|
||||
foreach (array_keys($config) as $compare) {
|
||||
// if string starts with:
|
||||
if (0 === strpos($version, $compare)) {
|
||||
|
||||
50
app/Events/DetectedNewIPAddress.php
Normal file
50
app/Events/DetectedNewIPAddress.php
Normal file
@@ -0,0 +1,50 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* DetectedNewIPAddress.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Events;
|
||||
|
||||
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
/**
|
||||
* Class DetectedNewIPAddress
|
||||
*/
|
||||
class DetectedNewIPAddress extends Event
|
||||
{
|
||||
use SerializesModels;
|
||||
|
||||
public string $ipAddress;
|
||||
public User $user;
|
||||
|
||||
/**
|
||||
* Create a new event instance. This event is triggered when a new user registers.
|
||||
*
|
||||
* @param User $user
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(User $user, string $ipAddress)
|
||||
{
|
||||
$this->ipAddress = $ipAddress;
|
||||
$this->user = $user;
|
||||
}
|
||||
}
|
||||
@@ -51,8 +51,8 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
* @param Request $request
|
||||
* @param Exception $exception
|
||||
*
|
||||
* @throws Exception
|
||||
* @return mixed
|
||||
* @throws Exception
|
||||
*/
|
||||
public function render($request, Throwable $exception)
|
||||
{
|
||||
@@ -75,7 +75,6 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
return $this->handleAccount($request, $exception);
|
||||
case 'transactions.show':
|
||||
return $this->handleGroup($request, $exception);
|
||||
break;
|
||||
case 'attachments.show':
|
||||
case 'attachments.edit':
|
||||
// redirect to original attachment holder.
|
||||
@@ -128,19 +127,21 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
case 'transactions.mass.edit':
|
||||
case 'transactions.mass.delete':
|
||||
case 'transactions.bulk.edit':
|
||||
$request->session()->reflash();
|
||||
|
||||
return redirect(route('index'));
|
||||
break;
|
||||
if ('POST' === $request->method()) {
|
||||
$request->session()->reflash();
|
||||
return redirect(route('index'));
|
||||
}
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Request $request
|
||||
* @param Throwable $exception
|
||||
*
|
||||
* @throws Exception
|
||||
* @return Redirector|Response
|
||||
* @throws Exception
|
||||
*/
|
||||
private function handleAccount(Request $request, Throwable $exception)
|
||||
{
|
||||
@@ -167,8 +168,8 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
* @param Request $request
|
||||
* @param Throwable $exception
|
||||
*
|
||||
* @throws Exception
|
||||
* @return RedirectResponse|Redirector|Response
|
||||
* @throws Exception
|
||||
*/
|
||||
private function handleAttachment(Request $request, Throwable $exception)
|
||||
{
|
||||
@@ -209,11 +210,11 @@ class GracefulNotFoundHandler extends ExceptionHandler
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Throwable $request
|
||||
* @param Throwable $request
|
||||
* @param Exception $exception
|
||||
*
|
||||
* @throws Exception
|
||||
* @return RedirectResponse|\Illuminate\Http\Response|Redirector|Response
|
||||
* @throws Exception
|
||||
*/
|
||||
private function handleGroup(Request $request, Throwable $exception)
|
||||
{
|
||||
|
||||
@@ -56,9 +56,6 @@ class AccountFactory
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
$this->canHaveVirtual = [AccountType::ASSET, AccountType::DEBT, AccountType::LOAN, AccountType::MORTGAGE, AccountType::CREDITCARD];
|
||||
$this->accountRepository = app(AccountRepositoryInterface::class);
|
||||
$this->validAssetFields = ['account_role', 'account_number', 'currency_id', 'BIC', 'include_net_worth'];
|
||||
|
||||
@@ -34,18 +34,6 @@ use Log;
|
||||
*/
|
||||
class AccountMetaFactory
|
||||
{
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
|
||||
@@ -36,21 +36,7 @@ use Log;
|
||||
*/
|
||||
class AttachmentFactory
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
|
||||
@@ -35,23 +35,7 @@ use Log;
|
||||
*/
|
||||
class TagFactory
|
||||
{
|
||||
/** @var Collection */
|
||||
private $tags;
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
|
||||
@@ -37,18 +37,6 @@ use Log;
|
||||
*/
|
||||
class TransactionCurrencyFactory
|
||||
{
|
||||
/**
|
||||
* TransactionCurrencyFactory constructor.
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $data
|
||||
*
|
||||
|
||||
@@ -258,7 +258,7 @@ class TransactionJournalFactory
|
||||
|
||||
/** Some basic fields */
|
||||
$type = $this->typeRepository->findTransactionType(null, $row['type']);
|
||||
$carbon = $row['date'] ?? new Carbon;
|
||||
$carbon = $row['date'] ?? today(config('app.timezone'));
|
||||
$order = $row['order'] ?? 0;
|
||||
$currency = $this->currencyRepository->findCurrency((int) $row['currency_id'], $row['currency_code']);
|
||||
$foreignCurrency = $this->currencyRepository->findCurrencyNull($row['foreign_currency_id'], $row['foreign_currency_code']);
|
||||
|
||||
@@ -138,16 +138,16 @@ class ChartJsGenerator implements GeneratorInterface
|
||||
'type' => $set['type'] ?? 'line',
|
||||
'data' => array_values($set['entries']),
|
||||
];
|
||||
if (isset($set['yAxisID'])) {
|
||||
if (array_key_exists('yAxisID', $set)) {
|
||||
$currentSet['yAxisID'] = $set['yAxisID'];
|
||||
}
|
||||
if (isset($set['fill'])) {
|
||||
if (array_key_exists('fill', $set)) {
|
||||
$currentSet['fill'] = $set['fill'];
|
||||
}
|
||||
if (isset($set['currency_symbol'])) {
|
||||
if (array_key_exists('currency_symbol', $set)) {
|
||||
$currentSet['currency_symbol'] = $set['currency_symbol'];
|
||||
}
|
||||
if (isset($set['backgroundColor'])) {
|
||||
if (array_key_exists('backgroundColor', $set)) {
|
||||
$currentSet['backgroundColor'] = $set['backgroundColor'];
|
||||
}
|
||||
$chartData['datasets'][] = $currentSet;
|
||||
|
||||
@@ -24,7 +24,9 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\StoredTransactionGroup;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -46,17 +48,26 @@ class StoredGroupEventHandler
|
||||
}
|
||||
Log::debug('Now in StoredGroupEventHandler::processRules()');
|
||||
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser($storedGroupEvent->transactionGroup->user);
|
||||
$ruleEngine->setAllRules(true);
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
|
||||
$journals = $storedGroupEvent->transactionGroup->transactionJournals;
|
||||
|
||||
$array = [];
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$ruleEngine->processTransactionJournal($journal);
|
||||
$array[] = $journal->id;
|
||||
}
|
||||
$journalIds = implode(',', $array);
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleRepository = app(RuleRepositoryInterface::class);
|
||||
$ruleRepository->setUser($storedGroupEvent->transactionGroup->user);
|
||||
$rules = $ruleRepository->getStoreRules();
|
||||
|
||||
// file rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($storedGroupEvent->transactionGroup->user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRules($rules);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,7 +27,9 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
@@ -88,17 +90,26 @@ class UpdatedGroupEventHandler
|
||||
return;
|
||||
}
|
||||
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser($updatedGroupEvent->transactionGroup->user);
|
||||
$ruleEngine->setAllRules(true);
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_UPDATE);
|
||||
$journals = $updatedGroupEvent->transactionGroup->transactionJournals;
|
||||
|
||||
$array = [];
|
||||
/** @var TransactionJournal $journal */
|
||||
foreach ($journals as $journal) {
|
||||
$ruleEngine->processTransactionJournal($journal);
|
||||
$array[] = $journal->id;
|
||||
}
|
||||
$journalIds = implode(',', $array);
|
||||
Log::debug(sprintf('Add local operator for journal(s): %s', $journalIds));
|
||||
|
||||
// collect rules:
|
||||
$ruleRepository = app(RuleRepositoryInterface::class);
|
||||
$ruleRepository->setUser($updatedGroupEvent->transactionGroup->user);
|
||||
$rules = $ruleRepository->getUpdateRules();
|
||||
|
||||
// file rule engine.
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($updatedGroupEvent->transactionGroup->user);
|
||||
$newRuleEngine->addOperator(['type' => 'journal_id', 'value' => $journalIds]);
|
||||
$newRuleEngine->setRules($rules);
|
||||
$newRuleEngine->fire();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,11 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Mail\ConfirmEmailChangeMail;
|
||||
use FireflyIII\Mail\NewIPAddressWarningMail;
|
||||
use FireflyIII\Mail\RegisteredUser as RegisteredUserMail;
|
||||
use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail;
|
||||
use FireflyIII\Mail\UndoEmailChangeMail;
|
||||
@@ -125,6 +128,78 @@ class UserEventHandler
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Login $event
|
||||
*/
|
||||
public function storeUserIPAddress(Login $event): void
|
||||
{
|
||||
/** @var User $user */
|
||||
$user = $event->user;
|
||||
/** @var array $preference */
|
||||
$preference = app('preferences')->getForUser($user, 'login_ip_history', [])->data;
|
||||
$inArray = false;
|
||||
$ip = request()->ip();
|
||||
Log::debug(sprintf('User logging in from IP address %s', $ip));
|
||||
|
||||
// update array if in array
|
||||
foreach ($preference as $index => $row) {
|
||||
if ($row['ip'] === $ip) {
|
||||
Log::debug('Found IP in array, refresh time.');
|
||||
$preference[$index]['time'] = now(config('app.timezone'))->format('Y-m-d H:i:s');
|
||||
$inArray = true;
|
||||
}
|
||||
// clean up old entries (6 months)
|
||||
$carbon = Carbon::createFromFormat('Y-m-d H:i:s', $preference[$index]['time']);
|
||||
if ($carbon->diffInMonths(today()) > 6) {
|
||||
Log::debug(sprintf('Entry for %s is very old, remove it.', $row['ip']));
|
||||
unset($preference[$index]);
|
||||
}
|
||||
}
|
||||
// add to array if not the case:
|
||||
if (false === $inArray) {
|
||||
$preference[] = [
|
||||
'ip' => $ip,
|
||||
'time' => now(config('app.timezone'))->format('Y-m-d H:i:s'),
|
||||
'notified' => false,
|
||||
];
|
||||
|
||||
|
||||
}
|
||||
$preference = array_values($preference);
|
||||
app('preferences')->setForUser($user, 'login_ip_history', $preference);
|
||||
|
||||
if (false === $inArray && true === config('firefly.warn_new_ip')) {
|
||||
event(new DetectedNewIPAddress($user, $ip));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param DetectedNewIPAddress $event
|
||||
*/
|
||||
public function notifyNewIPAddress(DetectedNewIPAddress $event): void
|
||||
{
|
||||
$user = $event->user;
|
||||
$email = $user->email;
|
||||
$ipAddress = $event->ipAddress;
|
||||
$list = app('preferences')->getForUser($user, 'login_ip_history', [])->data;
|
||||
|
||||
/** @var array $entry */
|
||||
foreach ($list as $index => $entry) {
|
||||
if (false === $entry['notified']) {
|
||||
try {
|
||||
Mail::to($email)->send(new NewIPAddressWarningMail($ipAddress));
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Exception $e) {
|
||||
Log::error($e->getMessage());
|
||||
}
|
||||
}
|
||||
$list[$index]['notified'] = true;
|
||||
}
|
||||
|
||||
app('preferences')->setForUser($user, 'login_ip_history', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email to confirm email change.
|
||||
*
|
||||
@@ -167,7 +242,7 @@ class UserEventHandler
|
||||
$ipAddress = $event->ipAddress;
|
||||
$token = app('preferences')->getForUser($user, 'email_change_undo_token', 'invalid');
|
||||
$hashed = hash('sha256', sprintf('%s%s', (string) config('app.key'), $oldEmail));
|
||||
$uri = route('profile.undo-email-change', [$token->data,$hashed]);
|
||||
$uri = route('profile.undo-email-change', [$token->data, $hashed]);
|
||||
try {
|
||||
Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $uri, $ipAddress));
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
@@ -306,7 +306,7 @@ class AttachmentHelper implements AttachmentHelperInterface
|
||||
$content = $fileObject->fread($file->getSize());
|
||||
Log::debug(sprintf('Full file length is %d and upload size is %d.', strlen($content), $file->getSize()));
|
||||
|
||||
// store it:
|
||||
// store it without encryption.
|
||||
$this->uploadDisk->put($attachment->fileName(), $content);
|
||||
$attachment->uploaded = true; // update attachment
|
||||
$attachment->save();
|
||||
|
||||
@@ -62,7 +62,7 @@ trait AmountCollection
|
||||
{
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($amount) {
|
||||
$q->where('destination.amount', '<', app('steam')->positive($amount));
|
||||
$q->where('destination.amount', '<=', app('steam')->positive($amount));
|
||||
}
|
||||
);
|
||||
|
||||
@@ -80,7 +80,7 @@ trait AmountCollection
|
||||
{
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) use ($amount) {
|
||||
$q->where('destination.amount', '>', app('steam')->positive($amount));
|
||||
$q->where('destination.amount', '>=', app('steam')->positive($amount));
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -61,6 +61,73 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesContain(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes.text', 'LIKE', sprintf('%%%s%%', $value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesEndWith(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes.text', 'LIKE', sprintf('%%%s', $value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutNotes(): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->whereNull('notes.text');
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withAnyNotes(): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->whereNotNull('notes.text');
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesExactly(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes.text', '=', sprintf('%s', $value));
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesStartWith(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->withNotes();
|
||||
$this->query->where('notes.text', 'LIKE', sprintf('%s%%', $value));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the search to a specific bill.
|
||||
*
|
||||
@@ -185,6 +252,32 @@ trait MetaCollection
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where has no tags.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutTags(): GroupCollectorInterface
|
||||
{
|
||||
$this->withTagInformation();
|
||||
$this->query->whereNull('tag_transaction_journal.tag_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Where has no tags.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAnyTag(): GroupCollectorInterface
|
||||
{
|
||||
$this->withTagInformation();
|
||||
$this->query->whereNotNull('tag_transaction_journal.tag_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will include bill name + ID, if any.
|
||||
*
|
||||
@@ -272,11 +365,20 @@ trait MetaCollection
|
||||
public function withoutBudget(): GroupCollectorInterface
|
||||
{
|
||||
$this->withBudgetInformation();
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('budget_transaction_journal.budget_id');
|
||||
}
|
||||
);
|
||||
$this->query->whereNull('budget_transaction_journal.budget_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a transactions without a budget..
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withBudget(): GroupCollectorInterface
|
||||
{
|
||||
$this->withBudgetInformation();
|
||||
$this->query->whereNotNull('budget_transaction_journal.budget_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
@@ -289,11 +391,20 @@ trait MetaCollection
|
||||
public function withoutCategory(): GroupCollectorInterface
|
||||
{
|
||||
$this->withCategoryInformation();
|
||||
$this->query->where(
|
||||
function (EloquentBuilder $q) {
|
||||
$q->whereNull('category_transaction_journal.category_id');
|
||||
}
|
||||
);
|
||||
$this->query->whereNull('category_transaction_journal.category_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit results to a transactions without a category.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withCategory(): GroupCollectorInterface
|
||||
{
|
||||
$this->withCategoryInformation();
|
||||
$this->query->whereNotNull('category_transaction_journal.category_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
*/
|
||||
public function dumpQuery(): void
|
||||
{
|
||||
echo $this->query->toSql();
|
||||
echo $this->query->select($this->fields)->toSql();
|
||||
echo '<pre>';
|
||||
print_r($this->query->getBindings());
|
||||
echo '</pre>';
|
||||
@@ -232,6 +232,16 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where('source.foreign_currency_id', $currency->id);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limit the result to a specific transaction group.
|
||||
*
|
||||
@@ -326,6 +336,79 @@ class GroupCollector implements GroupCollectorInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function descriptionStarts(array $array): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($array) {
|
||||
$q->where(
|
||||
static function (EloquentBuilder $q1) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%s%%', $word);
|
||||
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
$q->orWhere(
|
||||
static function (EloquentBuilder $q2) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%s%%', $word);
|
||||
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function descriptionEnds(array $array): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($array) {
|
||||
$q->where(
|
||||
static function (EloquentBuilder $q1) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s', $word);
|
||||
$q1->where('transaction_journals.description', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
$q->orWhere(
|
||||
static function (EloquentBuilder $q2) use ($array) {
|
||||
foreach ($array as $word) {
|
||||
$keyword = sprintf('%%%s', $word);
|
||||
$q2->where('transaction_groups.title', 'LIKE', $keyword);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function descriptionIs(string $value): GroupCollectorInterface
|
||||
{
|
||||
$this->query->where(
|
||||
static function (EloquentBuilder $q) use ($value) {
|
||||
$q->where('transaction_journals.description', '=', $value);
|
||||
$q->orWhere('transaction_groups.title', '=', $value);
|
||||
}
|
||||
);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
@@ -411,12 +494,26 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function convertToInteger(array $array): array
|
||||
{
|
||||
foreach ($this->integerFields as $field) {
|
||||
$array[$field] = isset($array[$field]) ? (int) $array[$field] : null;
|
||||
$array[$field] = array_key_exists($field, $array) ? (int) $array[$field] : null;
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Has attachments
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAttachments(): GroupCollectorInterface
|
||||
{
|
||||
Log::debug('Add filter on attachment ID.');
|
||||
$this->joinAttachmentTables();
|
||||
$this->query->whereNotNull('attachments.attachable_id');
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Join table to get attachment information.
|
||||
*/
|
||||
@@ -444,7 +541,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function mergeAttachments(array $existingJournal, TransactionJournal $newJournal): array
|
||||
{
|
||||
$newArray = $newJournal->toArray();
|
||||
if (isset($newArray['attachment_id'])) {
|
||||
if (array_key_exists('attachment_id', $newArray)) {
|
||||
$attachmentId = (int) $newJournal['tag_id'];
|
||||
$existingJournal['attachments'][$attachmentId] = [
|
||||
'id' => $attachmentId,
|
||||
@@ -463,7 +560,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
private function mergeTags(array $existingJournal, TransactionJournal $newJournal): array
|
||||
{
|
||||
$newArray = $newJournal->toArray();
|
||||
if (isset($newArray['tag_id'])) { // assume the other fields are present as well.
|
||||
if (array_key_exists('tag_id', $newArray)) { // assume the other fields are present as well.
|
||||
$tagId = (int) $newJournal['tag_id'];
|
||||
|
||||
$tagDate = null;
|
||||
@@ -496,7 +593,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
foreach ($collection as $augumentedJournal) {
|
||||
$groupId = $augumentedJournal->transaction_group_id;
|
||||
|
||||
if (!isset($groups[$groupId])) {
|
||||
if (!array_key_exists($groupId, $groups)) {
|
||||
// make new array
|
||||
$parsedGroup = $this->parseAugmentedJournal($augumentedJournal);
|
||||
$groupArray = [
|
||||
@@ -517,13 +614,13 @@ class GroupCollector implements GroupCollectorInterface
|
||||
$journalId = (int) $augumentedJournal->transaction_journal_id;
|
||||
|
||||
|
||||
if (isset($groups[$groupId]['transactions'][$journalId])) {
|
||||
if (array_key_exists($journalId, $groups[$groupId]['transactions'])) {
|
||||
// append data to existing group + journal (for multiple tags or multiple attachments)
|
||||
$groups[$groupId]['transactions'][$journalId] = $this->mergeTags($groups[$groupId]['transactions'][$journalId], $augumentedJournal);
|
||||
$groups[$groupId]['transactions'][$journalId] = $this->mergeAttachments($groups[$groupId]['transactions'][$journalId], $augumentedJournal);
|
||||
}
|
||||
|
||||
if (!isset($groups[$groupId]['transactions'][$journalId])) {
|
||||
if (!array_key_exists($journalId, $groups[$groupId]['transactions'])) {
|
||||
// create second, third, fourth split:
|
||||
$groups[$groupId]['count']++;
|
||||
$groups[$groupId]['transactions'][$journalId] = $this->parseAugmentedJournal($augumentedJournal);
|
||||
@@ -655,7 +752,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as source',
|
||||
function (JoinClause $join) {
|
||||
$join->on('source.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('source.amount', '<', 0);
|
||||
->where('source.amount', '<', 0);
|
||||
}
|
||||
)
|
||||
// join destination transaction
|
||||
@@ -663,7 +760,7 @@ class GroupCollector implements GroupCollectorInterface
|
||||
'transactions as destination',
|
||||
function (JoinClause $join) {
|
||||
$join->on('destination.transaction_journal_id', '=', 'transaction_journals.id')
|
||||
->where('destination.amount', '>', 0);
|
||||
->where('destination.amount', '>', 0);
|
||||
}
|
||||
)
|
||||
// left join transaction type.
|
||||
|
||||
@@ -220,6 +220,15 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setCurrency(TransactionCurrency $currency): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a specific foreign currency.
|
||||
*
|
||||
* @param TransactionCurrency $currency
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function setForeignCurrency(TransactionCurrency $currency): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Set destination accounts.
|
||||
*
|
||||
@@ -284,6 +293,33 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setSearchWords(array $array): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Beginning of the description must match:
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function descriptionStarts(array $array): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* End of the description must match:
|
||||
*
|
||||
* @param array $array
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function descriptionEnds(array $array): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Description must be:
|
||||
*
|
||||
* @param string $value
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function descriptionIs(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Set source accounts.
|
||||
*
|
||||
@@ -311,6 +347,16 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function setTags(Collection $tags): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutTags(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAnyTag(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit the search to one specific transaction group.
|
||||
*
|
||||
@@ -377,6 +423,13 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withAttachmentInformation(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Has attachments
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function hasAttachments(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Include bill name + ID.
|
||||
*
|
||||
@@ -405,6 +458,42 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withNotes(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Any notes, no matter what.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withAnyNotes(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesContain(string $value): GroupCollectorInterface;
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withoutNotes(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesStartWith(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesEndWith(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function notesExactly(string $value): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Add tag info.
|
||||
*
|
||||
@@ -426,6 +515,20 @@ interface GroupCollectorInterface
|
||||
*/
|
||||
public function withoutCategory(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a transactions with a category.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withCategory(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Limit results to a transactions with a budget.
|
||||
*
|
||||
* @return GroupCollectorInterface
|
||||
*/
|
||||
public function withBudget(): GroupCollectorInterface;
|
||||
|
||||
/**
|
||||
* Look for specific external ID's.
|
||||
*
|
||||
|
||||
@@ -117,7 +117,7 @@ class NetWorth implements NetWorthInterface
|
||||
|
||||
Log::debug(sprintf('Balance corrected to %s because of virtual balance (%s)', $balance, $virtualBalance));
|
||||
|
||||
if (!isset($netWorth[$currencyId])) {
|
||||
if (!array_key_exists($currencyId, $netWorth)) {
|
||||
$netWorth[$currencyId] = '0';
|
||||
}
|
||||
$netWorth[$currencyId] = bcadd($balance, $netWorth[$currencyId]);
|
||||
|
||||
@@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers\Account;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Account;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
@@ -32,7 +33,7 @@ use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Pagination\LengthAwarePaginator;
|
||||
use Illuminate\View\View;
|
||||
use Exception;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -41,8 +42,8 @@ use Exception;
|
||||
class IndexController extends Controller
|
||||
{
|
||||
use BasicDataSupport;
|
||||
/** @var AccountRepositoryInterface The account repository */
|
||||
private $repository;
|
||||
|
||||
private AccountRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* IndexController constructor.
|
||||
@@ -116,51 +117,37 @@ class IndexController extends Controller
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Foundation\Application|Factory|View
|
||||
*/
|
||||
public function emptyIndex(?string $objectType = null)
|
||||
{
|
||||
return view('accounts.empty-index', compact('objectType'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Show list of accounts.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param string $objectType
|
||||
*
|
||||
* @throws Exception
|
||||
* @return Factory|View
|
||||
* @throws Exception
|
||||
*/
|
||||
public function index(Request $request, string $objectType)
|
||||
{
|
||||
// temp catch for layout.
|
||||
if ('v2' === config('firefly.layout')) {
|
||||
return $this->emptyIndex($objectType);
|
||||
}
|
||||
|
||||
// reset account order:
|
||||
|
||||
Log::debug(sprintf('Now at %s', __METHOD__));
|
||||
$objectType = $objectType ?? 'asset';
|
||||
$subTitle = (string) trans(sprintf('firefly.%s_accounts', $objectType));
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $objectType));
|
||||
$types = config(sprintf('firefly.accountTypesByIdentifier.%s', $objectType));
|
||||
|
||||
if (1 === random_int(0, 20)) {
|
||||
Log::debug('Will reset order.');
|
||||
$this->repository->resetAccountOrder($types);
|
||||
}
|
||||
|
||||
$collection = $this->repository->getActiveAccountsByType($types);
|
||||
|
||||
|
||||
|
||||
$total = $collection->count();
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||
$accounts = $collection->slice(($page - 1) * $pageSize, $pageSize);
|
||||
$inactiveCount = $this->repository->getInactiveAccountsByType($types)->count();
|
||||
|
||||
Log::debug(sprintf('Count of collection: %d, count of accounts: %d', $total, $accounts->count()));
|
||||
|
||||
unset($collection);
|
||||
/** @var Carbon $start */
|
||||
$start = clone session('start', Carbon::now()->startOfMonth());
|
||||
@@ -187,9 +174,14 @@ class IndexController extends Controller
|
||||
}
|
||||
);
|
||||
// make paginator:
|
||||
Log::debug(sprintf('Count of accounts before LAP: %d', $accounts->count()));
|
||||
/** @var LengthAwarePaginator $accounts */
|
||||
$accounts = new LengthAwarePaginator($accounts, $total, $pageSize, $page);
|
||||
$accounts->setPath(route('accounts.index', [$objectType]));
|
||||
|
||||
Log::debug(sprintf('Count of accounts after LAP (1): %d', $accounts->count()));
|
||||
Log::debug(sprintf('Count of accounts after LAP (2): %d', $accounts->getCollection()->count()));
|
||||
|
||||
return view('accounts.index', compact('objectType', 'inactiveCount', 'subTitleIcon', 'subTitle', 'page', 'accounts'));
|
||||
}
|
||||
|
||||
|
||||
@@ -92,11 +92,6 @@ class ShowController extends Controller
|
||||
{
|
||||
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
|
||||
|
||||
// temp catch for layout.
|
||||
if ('v2' === config('firefly.layout')) {
|
||||
return view('accounts.empty-index', compact('objectType'));
|
||||
}
|
||||
|
||||
if (!$this->isEditableAccount($account)) {
|
||||
return $this->redirectAccountToAccount($account); // @codeCoverageIgnore
|
||||
}
|
||||
@@ -111,7 +106,7 @@ class ShowController extends Controller
|
||||
}
|
||||
$location = $this->repository->getLocation($account);
|
||||
$attachments = $this->repository->getAttachments($account);
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
$subTitleIcon = config(sprintf('firefly.subIconsByIdentifier.%s', $account->accountType->type));
|
||||
$page = (int) $request->get('page');
|
||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||
@@ -178,8 +173,8 @@ class ShowController extends Controller
|
||||
$isLiability = $this->repository->isLiability($account);
|
||||
$attachments = $this->repository->getAttachments($account);
|
||||
$objectType = config(sprintf('firefly.shortNamesByFullName.%s', $account->accountType->type));
|
||||
$end = new Carbon;
|
||||
$today = new Carbon;
|
||||
$end = today(config('app.timezone'));
|
||||
$today = today(config('app.timezone'));
|
||||
$start = $this->repository->oldestJournalDate($account) ?? Carbon::now()->startOfMonth();
|
||||
$subTitleIcon = config('firefly.subIconsByIdentifier.' . $account->accountType->type);
|
||||
$page = (int) $request->get('page');
|
||||
|
||||
@@ -87,7 +87,7 @@ class TelemetryController extends Controller
|
||||
public function submit()
|
||||
{
|
||||
$job = app(SubmitTelemetryData::class);
|
||||
$job->setDate(new Carbon);
|
||||
$job->setDate(today(config('app.timezone')));
|
||||
$job->setForce(true);
|
||||
$job->handle();
|
||||
session()->flash('info', trans('firefly.telemetry_submission_executed'));
|
||||
|
||||
@@ -61,9 +61,9 @@ class UpdateController extends Controller
|
||||
/**
|
||||
* Show page with update options.
|
||||
*
|
||||
* @throws NotFoundExceptionInterface
|
||||
* @throws ContainerExceptionInterface
|
||||
* @return Factory|View
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
@@ -100,6 +100,11 @@ class UpdateController extends Controller
|
||||
$checkForUpdates = (int) $request->get('check_for_updates');
|
||||
$channel = $request->get('update_channel');
|
||||
$channel = in_array($channel, ['stable', 'beta', 'alpha'], true) ? $channel : 'stable';
|
||||
|
||||
// store as telemetry
|
||||
app('telemetry')->feature('admin.update.channel', $channel);
|
||||
app('telemetry')->feature('admin.update.permission', (string) $checkForUpdates);
|
||||
|
||||
app('fireflyconfig')->set('permission_update_check', $checkForUpdates);
|
||||
app('fireflyconfig')->set('last_update_check', time());
|
||||
app('fireflyconfig')->set('update_channel', $channel);
|
||||
|
||||
@@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers\Admin;
|
||||
|
||||
use FireflyIII\Api\V1\Requests\UserUpdateRequest;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Middleware\IsDemoUser;
|
||||
use FireflyIII\Http\Requests\UserFormRequest;
|
||||
@@ -36,8 +37,8 @@ use Log;
|
||||
*/
|
||||
class UserController extends Controller
|
||||
{
|
||||
/** @var UserRepositoryInterface */
|
||||
private $repository;
|
||||
private UserRepositoryInterface $repository;
|
||||
protected bool $externalIdentity;
|
||||
|
||||
/**
|
||||
* UserController constructor.
|
||||
@@ -56,17 +57,23 @@ class UserController extends Controller
|
||||
}
|
||||
);
|
||||
$this->middleware(IsDemoUser::class)->except(['index', 'show']);
|
||||
$loginProvider = config('firefly.login_provider');
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
$this->externalIdentity = 'eloquent' !== $loginProvider || 'web' !== $authGuard;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user.
|
||||
*
|
||||
* @param User $user
|
||||
*
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
* @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|RedirectResponse|Redirector|\Illuminate\View\View
|
||||
*/
|
||||
public function delete(User $user)
|
||||
{
|
||||
if ($this->externalIdentity) {
|
||||
request()->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('admin.users'));
|
||||
}
|
||||
|
||||
$subTitle = (string) trans('firefly.delete_user', ['email' => $user->email]);
|
||||
|
||||
return view('admin.users.delete', compact('user', 'subTitle'));
|
||||
@@ -81,6 +88,11 @@ class UserController extends Controller
|
||||
*/
|
||||
public function destroy(User $user)
|
||||
{
|
||||
if ($this->externalIdentity) {
|
||||
request()->session()->flash('error', trans('firefly.external_user_mgt_disabled'));
|
||||
|
||||
return redirect(route('admin.users'));
|
||||
}
|
||||
$this->repository->destroy($user);
|
||||
session()->flash('success', (string) trans('firefly.user_deleted'));
|
||||
|
||||
@@ -96,6 +108,10 @@ class UserController extends Controller
|
||||
*/
|
||||
public function edit(User $user)
|
||||
{
|
||||
$canEditDetails = true;
|
||||
if ($this->externalIdentity) {
|
||||
$canEditDetails = false;
|
||||
}
|
||||
// put previous url in session if not redirect from store (not "return_to_edit").
|
||||
if (true !== session('users.edit.fromUpdate')) {
|
||||
$this->rememberPreviousUri('users.edit.uri');
|
||||
@@ -113,7 +129,7 @@ class UserController extends Controller
|
||||
'email_changed' => (string) trans('firefly.block_code_email_changed'),
|
||||
];
|
||||
|
||||
return view('admin.users.edit', compact('user', 'subTitle', 'subTitleIcon', 'codes', 'currentUser','isAdmin'));
|
||||
return view('admin.users.edit', compact('user', 'canEditDetails', 'subTitle', 'subTitleIcon', 'codes', 'currentUser', 'isAdmin'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,8 +195,10 @@ class UserController extends Controller
|
||||
Log::debug('Actually here');
|
||||
$data = $request->getUserData();
|
||||
|
||||
var_dump($data);
|
||||
|
||||
// update password
|
||||
if ('' !== $data['password']) {
|
||||
if (array_key_exists('password', $data) && '' !== $data['password']) {
|
||||
$this->repository->changePassword($user, $data['password']);
|
||||
}
|
||||
if (true === $data['is_owner']) {
|
||||
|
||||
@@ -66,13 +66,6 @@ class LoginController extends Controller
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('guest')->except('logout');
|
||||
|
||||
$loginProvider = config('firefly.login_provider');
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
|
||||
if ('eloquent' !== $loginProvider || 'web' !== $authGuard) {
|
||||
throw new FireflyException('Using external identity provider. Cannot continue.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -112,6 +105,8 @@ class LoginController extends Controller
|
||||
Log::channel('audit')->info(sprintf('User "%s" has been logged in.', $request->get('email')));
|
||||
Log::debug(sprintf('Redirect after login is %s.', $this->redirectPath()));
|
||||
|
||||
// if you just logged in, it can't be that you have a valid 2FA cookie.
|
||||
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
|
||||
@@ -132,7 +127,7 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function showLoginForm(Request $request)
|
||||
{
|
||||
Log::channel('audit')->info('Show login form (1.0).');
|
||||
Log::channel('audit')->info('Show login form (1.1).');
|
||||
|
||||
$count = DB::table('users')->count();
|
||||
$loginProvider = config('firefly.login_provider');
|
||||
@@ -158,7 +153,11 @@ class LoginController extends Controller
|
||||
$email = $request->old('email');
|
||||
$remember = $request->old('remember');
|
||||
|
||||
// todo must forget 2FA if user ends up here.
|
||||
$storeInCookie = config('google2fa.store_in_cookie', false);
|
||||
if (false !== $storeInCookie) {
|
||||
$cookieName = config('google2fa.cookie_name', 'google2fa_token');
|
||||
request()->cookies->set($cookieName, 'invalid');
|
||||
}
|
||||
|
||||
|
||||
return view('auth.login', compact('allowRegistration', 'email', 'remember', 'allowReset', 'title'));
|
||||
|
||||
@@ -36,21 +36,6 @@ use Preferences;
|
||||
*/
|
||||
class TwoFactorController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
|
||||
$loginProvider = config('firefly.login_provider');
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
|
||||
if ('eloquent' !== $loginProvider || 'web' !== $authGuard) {
|
||||
throw new FireflyException('Using external identity provider. Cannot continue.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What to do if 2FA lost?
|
||||
*
|
||||
|
||||
@@ -31,7 +31,7 @@ use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use FireflyIII\Transformers\AttachmentTransformer;
|
||||
use FireflyIII\Transformers\BillTransformer;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
@@ -73,14 +73,15 @@ class ShowController extends Controller
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rescan bills for transactions.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param Bill $bill
|
||||
*
|
||||
* @throws FireflyException
|
||||
* @return RedirectResponse|Redirector
|
||||
* @throws FireflyException
|
||||
*/
|
||||
public function rescan(Request $request, Bill $bill)
|
||||
{
|
||||
@@ -104,18 +105,13 @@ class ShowController extends Controller
|
||||
// unlink all journals:
|
||||
$this->repository->unlinkAll($bill);
|
||||
|
||||
foreach ($set as $rule) {
|
||||
// simply fire off all rules?
|
||||
/** @var TransactionMatcher $matcher */
|
||||
$matcher = app(TransactionMatcher::class);
|
||||
$matcher->setSearchLimit(100000); // large upper limit
|
||||
$matcher->setTriggeredLimit(100000); // large upper limit
|
||||
$matcher->setRule($rule);
|
||||
$matchingTransactions = $matcher->findTransactionsByRule();
|
||||
$total += count($matchingTransactions);
|
||||
$this->repository->linkCollectionToBill($bill, $matchingTransactions);
|
||||
}
|
||||
// fire the rules:
|
||||
/** @var RuleEngineInterface $ruleEngine */
|
||||
$ruleEngine = app(RuleEngineInterface::class);
|
||||
$ruleEngine->setRules($set);
|
||||
|
||||
// file the rule(s)
|
||||
$ruleEngine->fire();
|
||||
|
||||
$request->session()->flash('success', (string) trans_choice('firefly.rescanned_bill', $total));
|
||||
app('preferences')->mark();
|
||||
@@ -124,7 +120,6 @@ class ShowController extends Controller
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Show a bill.
|
||||
*
|
||||
|
||||
@@ -185,7 +185,7 @@ class AvailableBudgetController extends Controller
|
||||
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||
}
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
if (bccomp($amount, '0') <= 0) {
|
||||
session()->flash('error', trans('firefly.invalid_amount'));
|
||||
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||
@@ -198,7 +198,8 @@ class AvailableBudgetController extends Controller
|
||||
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||
}
|
||||
|
||||
$start->startOfDay();
|
||||
$end->endOfDay();
|
||||
// find existing AB
|
||||
$existing = $this->abRepository->find($currency, $start, $end);
|
||||
if (null === $existing) {
|
||||
@@ -238,7 +239,7 @@ class AvailableBudgetController extends Controller
|
||||
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||
}
|
||||
if (0 === bccomp('0', $amount)) {
|
||||
if (bccomp($amount, '0') <= 0) {
|
||||
session()->flash('error', trans('firefly.invalid_amount'));
|
||||
|
||||
return redirect(route('budgets.index', [$start->format('Y-m-d'), $end->format('Y-m-d')]));
|
||||
|
||||
@@ -52,14 +52,10 @@ class BudgetLimitController extends Controller
|
||||
{
|
||||
use DateCalculation;
|
||||
|
||||
/** @var BudgetLimitRepositoryInterface */
|
||||
private $blRepository;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepos;
|
||||
/** @var OperationsRepositoryInterface */
|
||||
private $opsRepository;
|
||||
/** @var BudgetRepositoryInterface The budget repository */
|
||||
private $repository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private CurrencyRepositoryInterface $currencyRepos;
|
||||
private OperationsRepositoryInterface $opsRepository;
|
||||
private BudgetRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* AmountController constructor.
|
||||
|
||||
@@ -39,11 +39,8 @@ use Illuminate\View\View;
|
||||
*/
|
||||
class CreateController extends Controller
|
||||
{
|
||||
/** @var BudgetRepositoryInterface The budget repository */
|
||||
private $repository;
|
||||
|
||||
/** @var AttachmentHelperInterface Helper for attachments. */
|
||||
private $attachments;
|
||||
private BudgetRepositoryInterface $repository;
|
||||
private AttachmentHelperInterface $attachments;
|
||||
|
||||
/**
|
||||
* CreateController constructor.
|
||||
|
||||
@@ -49,16 +49,11 @@ use Log;
|
||||
class IndexController extends Controller
|
||||
{
|
||||
use DateCalculation;
|
||||
/** @var AvailableBudgetRepositoryInterface */
|
||||
private $abRepository;
|
||||
/** @var BudgetLimitRepositoryInterface */
|
||||
private $blRepository;
|
||||
/** @var CurrencyRepositoryInterface */
|
||||
private $currencyRepository;
|
||||
/** @var OperationsRepositoryInterface */
|
||||
private $opsRepository;
|
||||
/** @var BudgetRepositoryInterface The budget repository */
|
||||
private $repository;
|
||||
private AvailableBudgetRepositoryInterface $abRepository;
|
||||
private BudgetLimitRepositoryInterface $blRepository;
|
||||
private CurrencyRepositoryInterface $currencyRepository;
|
||||
private OperationsRepositoryInterface $opsRepository;
|
||||
private BudgetRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* IndexController constructor.
|
||||
|
||||
@@ -123,7 +123,7 @@ class ShowController extends Controller
|
||||
$subTitle = (string) trans('firefly.all_journals_without_budget');
|
||||
$first = $this->journalRepos->firstNull();
|
||||
$start = null === $first ? new Carbon : $first->date;
|
||||
$end = new Carbon;
|
||||
$end = today(config('app.timezone'));
|
||||
$page = (int) $request->get('page');
|
||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||
|
||||
@@ -150,11 +150,7 @@ class ShowController extends Controller
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$allStart = session('first', Carbon::now()->startOfYear());
|
||||
$allEnd = new Carbon;
|
||||
|
||||
// use session range:
|
||||
$start = session('start');
|
||||
$end = session('end');
|
||||
$allEnd = today();
|
||||
|
||||
|
||||
$page = (int) $request->get('page');
|
||||
@@ -166,7 +162,7 @@ class ShowController extends Controller
|
||||
// collector:
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setRange($start, $end)->setBudget($budget)
|
||||
$collector->setRange($allStart, $allEnd)->setBudget($budget)
|
||||
->withAccountInformation()
|
||||
->setLimit($pageSize)->setPage($page)->withBudgetInformation()->withCategoryInformation();
|
||||
$groups = $collector->getPaginatedGroups();
|
||||
@@ -215,7 +211,7 @@ class ShowController extends Controller
|
||||
$groups->setPath(route('budgets.show', [$budget->id, $budgetLimit->id]));
|
||||
/** @var Carbon $start */
|
||||
$start = session('first', Carbon::now()->startOfYear());
|
||||
$end = new Carbon;
|
||||
$end = today(config('app.timezone'));
|
||||
$attachments = $this->repository->getAttachments($budget);
|
||||
$limits = $this->getLimits($budget, $start, $end);
|
||||
|
||||
|
||||
@@ -126,7 +126,7 @@ class NoCategoryController extends Controller
|
||||
$subTitle = (string) trans('firefly.all_journals_without_category');
|
||||
$first = $this->journalRepos->firstNull();
|
||||
$start = null === $first ? new Carbon : $first->date;
|
||||
$end = new Carbon;
|
||||
$end = today(config('app.timezone'));
|
||||
Log::debug(sprintf('Start for noCategory() is %s', $start->format('Y-m-d')));
|
||||
Log::debug(sprintf('End for noCategory() is %s', $end->format('Y-m-d')));
|
||||
|
||||
|
||||
@@ -129,8 +129,8 @@ class ShowController extends Controller
|
||||
$subTitle = (string) trans('firefly.all_journals_for_category', ['name' => $category->name]);
|
||||
$first = $this->repository->firstUseDate($category);
|
||||
/** @var Carbon $start */
|
||||
$start = $first ?? new Carbon;
|
||||
$end = new Carbon;
|
||||
$start = $first ?? today(config('app.timezone'));
|
||||
$end = today(config('app.timezone'));
|
||||
$path = route('categories.show.all', [$category->id]);
|
||||
$attachments = $this->repository->getAttachments($category);
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ use Illuminate\Support\Collection;
|
||||
class BudgetController extends Controller
|
||||
{
|
||||
use DateCalculation, AugumentData;
|
||||
|
||||
/** @var GeneratorInterface Chart generation methods. */
|
||||
protected $generator;
|
||||
/** @var OperationsRepositoryInterface */
|
||||
@@ -92,9 +93,9 @@ class BudgetController extends Controller
|
||||
public function budget(Budget $budget): JsonResponse
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = $this->repository->firstUseDate($budget) ?? session('start', new Carbon);
|
||||
$start = $this->repository->firstUseDate($budget) ?? session('start', today(config('app.timezone')));
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', new Carbon);
|
||||
$end = session('end', today(config('app.timezone')));
|
||||
$cache = new CacheProperties();
|
||||
$cache->addProperty($start);
|
||||
$cache->addProperty($end);
|
||||
@@ -102,7 +103,7 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($budget->id);
|
||||
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
//return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$step = $this->calculateStep($start, $end); // depending on diff, do something with range of chart.
|
||||
$collection = new Collection([$budget]);
|
||||
@@ -114,9 +115,6 @@ class BudgetController extends Controller
|
||||
while ($end >= $loopStart) {
|
||||
/** @var Carbon $currentEnd */
|
||||
$loopEnd = app('navigation')->endOfPeriod($loopStart, $step);
|
||||
if ('1Y' === $step) {
|
||||
$loopEnd->subDay(); // @codeCoverageIgnore
|
||||
}
|
||||
$spent = $this->opsRepository->sumExpenses($loopStart, $loopEnd, null, $collection);
|
||||
$label = trim(app('navigation')->periodShow($loopStart, $step));
|
||||
|
||||
@@ -141,7 +139,7 @@ class BudgetController extends Controller
|
||||
'entries' => $defaultEntries,
|
||||
];
|
||||
foreach ($currency['spent'] as $label => $spent) {
|
||||
$chartData[$currencyId]['entries'][$label] = round(bcmul($spent, '-1'), $currency['currency_decimal_places']);
|
||||
$chartData[$currencyId]['entries'][$label] = bcmul($spent, '-1');
|
||||
}
|
||||
}
|
||||
$data = $this->generator->multiSet(array_values($chartData));
|
||||
@@ -179,19 +177,19 @@ class BudgetController extends Controller
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$locale = app('steam')->getLocale();
|
||||
$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', [], $locale));
|
||||
$format = $start->formatLocalized((string) trans('config.month_and_day', [], $locale));
|
||||
$entries[$format] = $amount;
|
||||
|
||||
$start->addDay();
|
||||
}
|
||||
$data = $this->generator->singleSet((string)trans('firefly.left'), $entries);
|
||||
$data = $this->generator->singleSet((string) trans('firefly.left'), $entries);
|
||||
// add currency symbol from budget limit:
|
||||
$data['datasets'][0]['currency_symbol'] = $budgetLimit->transactionCurrency->symbol;
|
||||
$data['datasets'][0]['currency_code'] = $budgetLimit->transactionCurrency->code;
|
||||
@@ -218,8 +216,9 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty($budgetLimitId);
|
||||
$cache->addProperty('chart.budget.expense-asset');
|
||||
$start = session()->get('start');
|
||||
$end = session()->get('end');
|
||||
$start = session('first', Carbon::now()->startOfYear());
|
||||
$end = today();
|
||||
|
||||
if (null !== $budgetLimit) {
|
||||
$start = $budgetLimit->start_date;
|
||||
$end = $budgetLimit->end_date;
|
||||
@@ -239,7 +238,7 @@ class BudgetController extends Controller
|
||||
|
||||
// group by asset account ID:
|
||||
foreach ($journals as $journal) {
|
||||
$key = sprintf('%d-%d', (int)$journal['source_account_id'], $journal['currency_id']);
|
||||
$key = sprintf('%d-%d', (int) $journal['source_account_id'], $journal['currency_id']);
|
||||
$result[$key] = $result[$key] ?? [
|
||||
'amount' => '0',
|
||||
'currency_symbol' => $journal['currency_symbol'],
|
||||
@@ -252,13 +251,13 @@ class BudgetController extends Controller
|
||||
$names = $this->getAccountNames(array_keys($result));
|
||||
foreach ($result as $combinedId => $info) {
|
||||
$parts = explode('-', $combinedId);
|
||||
$assetId = (int)$parts[0];
|
||||
$assetId = (int) $parts[0];
|
||||
$title = sprintf('%s (%s)', $names[$assetId] ?? '(empty)', $info['currency_name']);
|
||||
$chartData[$title]
|
||||
= [
|
||||
'amount' => $info['amount'],
|
||||
'currency_symbol' => $info['currency_symbol'],
|
||||
'currency_code' => $info['currency_code'],
|
||||
'currency_code' => $info['currency_code'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -286,8 +285,8 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty($budgetLimitId);
|
||||
$cache->addProperty('chart.budget.expense-category');
|
||||
$start = session()->get('start');
|
||||
$end = session()->get('end');
|
||||
$start = session('first', Carbon::now()->startOfYear());
|
||||
$end = today();
|
||||
if (null !== $budgetLimit) {
|
||||
$start = $budgetLimit->start_date;
|
||||
$end = $budgetLimit->end_date;
|
||||
@@ -319,12 +318,12 @@ class BudgetController extends Controller
|
||||
$names = $this->getCategoryNames(array_keys($result));
|
||||
foreach ($result as $combinedId => $info) {
|
||||
$parts = explode('-', $combinedId);
|
||||
$categoryId = (int)$parts[0];
|
||||
$categoryId = (int) $parts[0];
|
||||
$title = sprintf('%s (%s)', $names[$categoryId] ?? '(empty)', $info['currency_name']);
|
||||
$chartData[$title] = [
|
||||
'amount' => $info['amount'],
|
||||
'currency_symbol' => $info['currency_symbol'],
|
||||
'currency_code' => $info['currency_code'],
|
||||
'currency_code' => $info['currency_code'],
|
||||
];
|
||||
}
|
||||
$data = $this->generator->multiCurrencyPieChart($chartData);
|
||||
@@ -352,8 +351,8 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($budget->id);
|
||||
$cache->addProperty($budgetLimitId);
|
||||
$cache->addProperty('chart.budget.expense-expense');
|
||||
$start = session()->get('start');
|
||||
$end = session()->get('end');
|
||||
$start = session('first', Carbon::now()->startOfYear());
|
||||
$end = today();
|
||||
if (null !== $budgetLimit) {
|
||||
$start = $budgetLimit->start_date;
|
||||
$end = $budgetLimit->end_date;
|
||||
@@ -385,13 +384,13 @@ class BudgetController extends Controller
|
||||
$names = $this->getAccountNames(array_keys($result));
|
||||
foreach ($result as $combinedId => $info) {
|
||||
$parts = explode('-', $combinedId);
|
||||
$opposingId = (int)$parts[0];
|
||||
$opposingId = (int) $parts[0];
|
||||
$name = $names[$opposingId] ?? 'no name';
|
||||
$title = sprintf('%s (%s)', $name, $info['currency_name']);
|
||||
$chartData[$title] = [
|
||||
'amount' => $info['amount'],
|
||||
'currency_symbol' => $info['currency_symbol'],
|
||||
'currency_code' => $info['currency_code'],
|
||||
'currency_code' => $info['currency_code'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -421,13 +420,13 @@ class BudgetController extends Controller
|
||||
$cache->addProperty($end);
|
||||
$cache->addProperty('chart.budget.frontpage');
|
||||
if ($cache->has()) {
|
||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
return response()->json($cache->get()); // @codeCoverageIgnore
|
||||
}
|
||||
$budgets = $this->repository->getActiveBudgets();
|
||||
$chartData = [
|
||||
['label' => (string)trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string)trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string)trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.spent_in_budget'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.left_to_spend'), 'entries' => [], 'type' => 'bar'],
|
||||
['label' => (string) trans('firefly.overspent'), 'entries' => [], 'type' => 'bar'],
|
||||
];
|
||||
|
||||
/** @var Budget $budget */
|
||||
@@ -439,8 +438,8 @@ class BudgetController extends Controller
|
||||
foreach ($spent as $entry) {
|
||||
$title = sprintf('%s (%s)', $budget->name, $entry['currency_name']);
|
||||
$chartData[0]['entries'][$title] = bcmul($entry['sum'], '-1'); // spent
|
||||
$chartData[1]['entries'][$title] = 0; // left to spend
|
||||
$chartData[2]['entries'][$title] = 0; // overspent
|
||||
$chartData[1]['entries'][$title] = 0; // left to spend
|
||||
$chartData[2]['entries'][$title] = 0; // overspent
|
||||
}
|
||||
}
|
||||
if (0 !== $limits->count()) {
|
||||
@@ -596,7 +595,7 @@ class BudgetController extends Controller
|
||||
$currentStart = app('navigation')->addPeriod($currentStart, $preferredRange, 0);
|
||||
}
|
||||
|
||||
$data = $this->generator->singleSet((string)trans('firefly.spent'), $chartData);
|
||||
$data = $this->generator->singleSet((string) trans('firefly.spent'), $chartData);
|
||||
$cache->store($data);
|
||||
|
||||
return response()->json($data);
|
||||
|
||||
@@ -230,7 +230,7 @@ class CategoryController extends Controller
|
||||
{
|
||||
$carbon = null;
|
||||
try {
|
||||
$carbon = new Carbon;
|
||||
$carbon = today(config('app.timezone'));
|
||||
} catch (Exception $e) {
|
||||
$e->getMessage();
|
||||
}
|
||||
|
||||
@@ -79,7 +79,7 @@ class PiggyBankController extends Controller
|
||||
$locale =app('steam')->getLocale();
|
||||
|
||||
// get first event or start date of piggy bank or today
|
||||
$startDate = $piggyBank->start_date ?? new Carbon;
|
||||
$startDate = $piggyBank->start_date ?? today(config('app.timezone'));
|
||||
|
||||
/** @var PiggyBankEvent $first */
|
||||
$firstEvent = $set->first();
|
||||
@@ -87,7 +87,7 @@ class PiggyBankController extends Controller
|
||||
|
||||
// which ever is older:
|
||||
$oldest = $startDate->lt($firstDate) ? $startDate : $firstDate;
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
// depending on diff, do something with range of chart.
|
||||
$step = $this->calculateStep($oldest, $today);
|
||||
|
||||
|
||||
@@ -142,6 +142,10 @@ class DebugController extends Controller
|
||||
$bcscale = bcscale();
|
||||
$layout = env('FIREFLY_III_LAYOUT');
|
||||
|
||||
// expected + found DB version:
|
||||
$expectedDBversion = config('firefly.db_version');
|
||||
$foundDBversion = \FireflyConfig::get('db_version',1)->data;
|
||||
|
||||
// some new vars.
|
||||
$telemetry = true === config('firefly.send_telemetry') && true === config('firefly.feature_flags.telemetry');
|
||||
$defaultLanguage = (string) config('firefly.default_language');
|
||||
@@ -190,6 +194,8 @@ class DebugController extends Controller
|
||||
compact(
|
||||
'phpVersion',
|
||||
'localeAttempts',
|
||||
'expectedDBversion',
|
||||
'foundDBversion',
|
||||
'appEnv',
|
||||
'appDebug',
|
||||
'logChannel',
|
||||
|
||||
@@ -79,7 +79,7 @@ class IndexController extends Controller
|
||||
$generator->setExportTransactions(true);
|
||||
|
||||
// get first transaction in DB:
|
||||
$firstDate = new Carbon;
|
||||
$firstDate = today(config('app.timezone'));
|
||||
$firstDate->subYear();
|
||||
$journal = $this->journalRepository->firstNull();
|
||||
if (null !== $journal) {
|
||||
|
||||
@@ -126,7 +126,7 @@ class HomeController extends Controller
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
/** @noinspection NullPointerExceptionInspection */
|
||||
$accounts = $repository->getAccountsById($frontPage->data);
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
|
||||
/** @var BillRepositoryInterface $billRepository */
|
||||
$billRepository = app(BillRepositoryInterface::class);
|
||||
|
||||
@@ -68,7 +68,7 @@ class BoxController extends Controller
|
||||
$start = session('start', Carbon::now()->startOfMonth());
|
||||
/** @var Carbon $end */
|
||||
$end = session('end', Carbon::now()->endOfMonth());
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
$display = 2; // see method docs.
|
||||
$boxTitle = (string) trans('firefly.spent');
|
||||
|
||||
|
||||
@@ -72,25 +72,24 @@ class RuleController extends Controller
|
||||
*/
|
||||
public function trigger(Request $request): JsonResponse
|
||||
{
|
||||
$count = (int) $request->get('count') > 0 ? (int) $request->get('count') : 1;
|
||||
$keys = array_keys(config('firefly.rule-triggers'));
|
||||
$triggers = [];
|
||||
foreach ($keys as $key) {
|
||||
if ('user_action' !== $key) {
|
||||
$triggers[$key] = (string) trans('firefly.rule_trigger_' . $key . '_choice');
|
||||
$count = (int) $request->get('count') > 0 ? (int) $request->get('count') : 1;
|
||||
$operators = config('firefly.search.operators');
|
||||
$triggers = [];
|
||||
foreach ($operators as $key => $operator) {
|
||||
if ('user_action' !== $key && false === $operator['alias']) {
|
||||
|
||||
$triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key));
|
||||
}
|
||||
}
|
||||
asort($triggers);
|
||||
|
||||
try {
|
||||
$view = view('rules.partials.trigger', compact('triggers', 'count'))->render();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $e) {
|
||||
Log::error(sprintf('Cannot render rules.partials.trigger: %s', $e->getMessage()));
|
||||
$view = 'Could not render view.';
|
||||
}
|
||||
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
return response()->json(['html' => $view]);
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ class AmountController extends Controller
|
||||
*/
|
||||
public function add(PiggyBank $piggyBank)
|
||||
{
|
||||
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, new Carbon);
|
||||
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, today(config('app.timezone')));
|
||||
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank);
|
||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
||||
$maxAmount = min($leftOnAccount, $leftToSave);
|
||||
@@ -95,7 +95,7 @@ class AmountController extends Controller
|
||||
public function addMobile(PiggyBank $piggyBank)
|
||||
{
|
||||
/** @var Carbon $date */
|
||||
$date = session('end', new Carbon);
|
||||
$date = session('end', today(config('app.timezone')));
|
||||
$leftOnAccount = $this->piggyRepos->leftOnAccount($piggyBank, $date);
|
||||
$savedSoFar = $this->piggyRepos->getCurrentAmount($piggyBank);
|
||||
$leftToSave = bcsub($piggyBank->targetamount, $savedSoFar);
|
||||
|
||||
@@ -96,7 +96,7 @@ class CreateController extends Controller
|
||||
{
|
||||
$data = $request->getPiggyBankData();
|
||||
if (null === $data['startdate']) {
|
||||
$data['startdate'] = new Carbon;
|
||||
$data['startdate'] = today(config('app.timezone'));
|
||||
}
|
||||
$piggyBank = $this->piggyRepos->store($data);
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ class ProfileController extends Controller
|
||||
$loginProvider = config('firefly.login_provider');
|
||||
$authGuard = config('firefly.authentication_guard');
|
||||
$this->externalIdentity = 'eloquent' !== $loginProvider || 'web' !== $authGuard;
|
||||
Log::debug(sprintf('ProfileController::__construct(). Login provider is "%s", authentication guard is "%s"',$loginProvider, $authGuard));
|
||||
|
||||
$this->middleware(IsDemoUser::class)->except(['index']);
|
||||
}
|
||||
@@ -345,7 +346,6 @@ class ProfileController extends Controller
|
||||
$userId = $user->id;
|
||||
$enabled2FA = null !== $user->mfa_secret;
|
||||
$mfaBackupCount = count(app('preferences')->get('mfa_recovery', [])->data);
|
||||
|
||||
$this->createOAuthKeys();
|
||||
|
||||
if (0 === $count) {
|
||||
|
||||
@@ -85,7 +85,7 @@ class CreateController extends Controller
|
||||
{
|
||||
$budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$tomorrow = new Carbon;
|
||||
$tomorrow = today(config('app.timezone'));
|
||||
$oldRepetitionType = $request->old('repetition_type');
|
||||
$tomorrow->addDay();
|
||||
|
||||
@@ -179,7 +179,7 @@ class CreateController extends Controller
|
||||
{
|
||||
$budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets());
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$tomorrow = new Carbon;
|
||||
$tomorrow = today(config('app.timezone'));
|
||||
$oldRepetitionType = $request->old('repetition_type');
|
||||
$tomorrow->addDay();
|
||||
|
||||
|
||||
@@ -85,8 +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;
|
||||
$today = today(config('app.timezone'));
|
||||
$year = today(config('app.timezone'));
|
||||
|
||||
// split collection
|
||||
$total = $collection->count();
|
||||
|
||||
@@ -84,7 +84,7 @@ class ShowController extends Controller
|
||||
|
||||
$array = $transformer->transform($recurrence);
|
||||
$groups = $this->recurring->getTransactions($recurrence);
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
$array['repeat_until'] = null !== $array['repeat_until'] ? new Carbon($array['repeat_until']) : null;
|
||||
|
||||
// transform dates back to Carbon objects:
|
||||
|
||||
@@ -263,7 +263,7 @@ class ReportController extends Controller
|
||||
public function index(AccountRepositoryInterface $repository)
|
||||
{
|
||||
/** @var Carbon $start */
|
||||
$start = clone session('first', new Carbon);
|
||||
$start = clone session('first', today(config('app.timezone')));
|
||||
$months = $this->helper->listOfMonths($start);
|
||||
$customFiscalYear = app('preferences')->get('customFiscalYear', 0)->data;
|
||||
$accounts = $repository->getAccountsByType(
|
||||
|
||||
@@ -32,11 +32,15 @@ use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Controllers\ModelInformation;
|
||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use Illuminate\View\View;
|
||||
use Log;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* Class CreateController
|
||||
@@ -44,8 +48,8 @@ use Illuminate\View\View;
|
||||
class CreateController extends Controller
|
||||
{
|
||||
use RuleManagement, ModelInformation;
|
||||
/** @var RuleRepositoryInterface Rule repository */
|
||||
private $ruleRepos;
|
||||
|
||||
private RuleRepositoryInterface $ruleRepos;
|
||||
|
||||
/**
|
||||
* RuleController constructor.
|
||||
@@ -71,8 +75,8 @@ class CreateController extends Controller
|
||||
/**
|
||||
* Create a new rule. It will be stored under the given $ruleGroup.
|
||||
*
|
||||
* @param Request $request
|
||||
* @param RuleGroup $ruleGroup
|
||||
* @param Request $request
|
||||
* @param RuleGroup|null $ruleGroup
|
||||
*
|
||||
* @return Factory|View
|
||||
*/
|
||||
@@ -86,6 +90,20 @@ class CreateController extends Controller
|
||||
$oldTriggers = [];
|
||||
$oldActions = [];
|
||||
|
||||
// build triggers from query, if present.
|
||||
$query = (string) $request->get('from_query');
|
||||
if ('' !== $query) {
|
||||
$search = app(SearchInterface::class);
|
||||
$search->parseQuery($query);
|
||||
$words = $search->getWordsAsString();
|
||||
$operators = $search->getOperators()->toArray();
|
||||
if ('' !== $words) {
|
||||
session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => $words]));
|
||||
array_push($operators, ['type' => 'description_contains', 'value' => $words]);
|
||||
}
|
||||
$oldTriggers = $this->parseFromOperators($operators);
|
||||
}
|
||||
|
||||
// restore actions and triggers from old input:
|
||||
if ($request->old()) {
|
||||
$oldTriggers = $this->getPreviousTriggers($request);
|
||||
@@ -143,6 +161,12 @@ class CreateController extends Controller
|
||||
$oldTriggers = $this->getTriggersForBill($bill);
|
||||
$oldActions = $this->getActionsForBill($bill);
|
||||
|
||||
// restore actions and triggers from old input:
|
||||
if ($request->old()) {
|
||||
$oldTriggers = $this->getPreviousTriggers($request);
|
||||
$oldActions = $this->getPreviousActions($request);
|
||||
}
|
||||
|
||||
$triggerCount = count($oldTriggers);
|
||||
$actionCount = count($oldActions);
|
||||
$subTitleIcon = 'fa-clone';
|
||||
@@ -177,10 +201,8 @@ class CreateController extends Controller
|
||||
$subTitle = (string) trans('firefly.make_new_rule_no_group');
|
||||
|
||||
// get triggers and actions for journal.
|
||||
$oldTriggers = $this->getTriggersForJournal($journal);
|
||||
$oldActions = [];
|
||||
$triggerCount = count($oldTriggers);
|
||||
$actionCount = count($oldActions);
|
||||
$oldTriggers = $this->getTriggersForJournal($journal);
|
||||
$oldActions = [];
|
||||
|
||||
$this->createDefaultRuleGroup();
|
||||
$this->createDefaultRule();
|
||||
@@ -192,6 +214,15 @@ class CreateController extends Controller
|
||||
'description' => (string) trans('firefly.new_rule_for_journal_description', ['description' => $journal->description]),
|
||||
];
|
||||
|
||||
// restore actions and triggers from old input:
|
||||
if ($request->old()) {
|
||||
$oldTriggers = $this->getPreviousTriggers($request);
|
||||
$oldActions = $this->getPreviousActions($request);
|
||||
}
|
||||
|
||||
$triggerCount = count($oldTriggers);
|
||||
$actionCount = count($oldActions);
|
||||
|
||||
// flash old data
|
||||
$request->session()->flash('preFilled', $preFilled);
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Controllers\RenderPartialViews;
|
||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
@@ -81,15 +83,31 @@ class EditController extends Controller
|
||||
$actionCount = 0;
|
||||
$oldActions = [];
|
||||
$oldTriggers = [];
|
||||
|
||||
// build triggers from query, if present.
|
||||
$query = (string) $request->get('from_query');
|
||||
if ('' !== $query) {
|
||||
$search = app(SearchInterface::class);
|
||||
$search->parseQuery($query);
|
||||
$words = $search->getWordsAsString();
|
||||
$operators = $search->getOperators()->toArray();
|
||||
if ('' !== $words) {
|
||||
session()->flash('warning', trans('firefly.rule_from_search_words', ['string' => $words]));
|
||||
array_push($operators, ['type' => 'description_contains', 'value' => $words]);
|
||||
}
|
||||
$oldTriggers = $this->parseFromOperators($operators);
|
||||
}
|
||||
|
||||
|
||||
// has old input?
|
||||
if (count($request->old()) > 0) {
|
||||
$oldTriggers = $this->getPreviousTriggers($request);
|
||||
$triggerCount = count($oldTriggers);
|
||||
$oldActions = $this->getPreviousActions($request);
|
||||
$actionCount = count($oldActions);
|
||||
}
|
||||
$triggerCount = count($oldTriggers);
|
||||
$actionCount = count($oldActions);
|
||||
|
||||
// overrule old input when it has no rule data:
|
||||
// overrule old input and query data when it has no rule data:
|
||||
if (0 === $triggerCount && 0 === $actionCount) {
|
||||
$oldTriggers = $this->getCurrentTriggers($rule);
|
||||
$triggerCount = count($oldTriggers);
|
||||
@@ -146,4 +164,45 @@ class EditController extends Controller
|
||||
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $submittedOperators
|
||||
* @return array
|
||||
*/
|
||||
private function parseFromOperators(array $submittedOperators): array
|
||||
{
|
||||
// TODO duplicated code.
|
||||
$operators = config('firefly.search.operators');
|
||||
$renderedEntries = [];
|
||||
$triggers = [];
|
||||
foreach ($operators as $key => $operator) {
|
||||
if ('user_action' !== $key && false === $operator['alias']) {
|
||||
|
||||
$triggers[$key] = (string) trans(sprintf('firefly.rule_trigger_%s_choice', $key));
|
||||
}
|
||||
}
|
||||
asort($triggers);
|
||||
|
||||
$index = 0;
|
||||
foreach ($submittedOperators as $operator) {
|
||||
try {
|
||||
$renderedEntries[] = view(
|
||||
'rules.partials.trigger',
|
||||
[
|
||||
'oldTrigger' => OperatorQuerySearch::getRootOperator($operator['type']),
|
||||
'oldValue' => $operator['value'],
|
||||
'oldChecked' => 1 === (int) ($oldTrigger['stop_processing'] ?? '0'),
|
||||
'count' => $index + 1,
|
||||
'triggers' => $triggers,
|
||||
]
|
||||
)->render();
|
||||
} catch (Throwable $e) {
|
||||
Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage()));
|
||||
Log::error($e->getTraceAsString());
|
||||
}
|
||||
$index++;
|
||||
}
|
||||
|
||||
return $renderedEntries;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,12 +25,15 @@ namespace FireflyIII\Http\Controllers\Rule;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\RuleTrigger;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
@@ -40,10 +43,9 @@ use Illuminate\View\View;
|
||||
class IndexController extends Controller
|
||||
{
|
||||
use RuleManagement;
|
||||
/** @var RuleGroupRepositoryInterface Rule group repository */
|
||||
private $ruleGroupRepos;
|
||||
/** @var RuleRepositoryInterface Rule repository. */
|
||||
private $ruleRepos;
|
||||
|
||||
private RuleGroupRepositoryInterface $ruleGroupRepos;
|
||||
private RuleRepositoryInterface $ruleRepos;
|
||||
|
||||
/**
|
||||
* RuleController constructor.
|
||||
@@ -82,6 +84,20 @@ class IndexController extends Controller
|
||||
return view('rules.index', compact('ruleGroups'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Rule $rule
|
||||
* @return RedirectResponse
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
*/
|
||||
public function search(Rule $rule): RedirectResponse
|
||||
{
|
||||
$route = route('search.index');
|
||||
$query = $this->ruleRepos->getSearchQuery($rule);
|
||||
$route = sprintf('%s?%s', $route, http_build_query(['search' => $query, 'rule' => $rule->id]));
|
||||
|
||||
return redirect($route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop action for reordering of rule actions.
|
||||
*
|
||||
|
||||
@@ -25,17 +25,14 @@ namespace FireflyIII\Http\Controllers\Rule;
|
||||
|
||||
|
||||
use Carbon\Carbon;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||
use FireflyIII\Http\Requests\TestRuleFormRequest;
|
||||
use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Models\RuleTrigger;
|
||||
use FireflyIII\Support\Http\Controllers\RequestInformation;
|
||||
use FireflyIII\Support\Http\Controllers\RuleManagement;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
@@ -53,8 +50,6 @@ use Throwable;
|
||||
class SelectController extends Controller
|
||||
{
|
||||
use RuleManagement, RequestInformation;
|
||||
/** @var AccountRepositoryInterface The account repository */
|
||||
private $accountRepos;
|
||||
|
||||
/**
|
||||
* RuleController constructor.
|
||||
@@ -68,8 +63,6 @@ class SelectController extends Controller
|
||||
app('view')->share('title', (string) trans('firefly.rules'));
|
||||
app('view')->share('mainTitleIcon', 'fa-random');
|
||||
|
||||
$this->accountRepos = app(AccountRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
);
|
||||
@@ -88,29 +81,22 @@ class SelectController extends Controller
|
||||
// Get parameters specified by the user
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$accounts = $this->accountRepos->getAccountsById($request->get('accounts'));
|
||||
$startDate = new Carbon($request->get('start_date'));
|
||||
$endDate = new Carbon($request->get('end_date'));
|
||||
$rules = [$rule->id];
|
||||
$accounts = implode(',', $request->get('accounts'));
|
||||
$startDate = new Carbon($request->get('start'));
|
||||
$endDate = new Carbon($request->get('end'));
|
||||
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser(auth()->user());
|
||||
$ruleEngine->setRulesToApply($rules);
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_BOTH);
|
||||
// create new rule engine:
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts($accounts);
|
||||
$collector->setRange($startDate, $endDate);
|
||||
$journals = $collector->getExtractedJournals();
|
||||
// add extra operators:
|
||||
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
|
||||
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
|
||||
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
Log::debug('Start of new journal.');
|
||||
$ruleEngine->processJournalArray($journal);
|
||||
Log::debug('Done with all rules for this group + done with journal.');
|
||||
}
|
||||
// set rules:
|
||||
$newRuleEngine->setRules(new Collection([$rule]));
|
||||
$newRuleEngine->fire();
|
||||
|
||||
// Tell the user that the job is queued
|
||||
session()->flash('success', (string) trans('firefly.applied_rule_selection', ['title' => $rule->title]));
|
||||
@@ -128,8 +114,8 @@ class SelectController extends Controller
|
||||
*/
|
||||
public function selectTransactions(Rule $rule)
|
||||
{
|
||||
if(false===$rule->active) {
|
||||
session()->flash('warning',trans('firefly.cannot_fire_inactive_rules'));
|
||||
if (false === $rule->active) {
|
||||
session()->flash('warning', trans('firefly.cannot_fire_inactive_rules'));
|
||||
return redirect(route('rules.index'));
|
||||
}
|
||||
// does the user have shared accounts?
|
||||
@@ -144,10 +130,6 @@ class SelectController extends Controller
|
||||
* This method allows the user to test a certain set of rule triggers. The rule triggers are passed along
|
||||
* using the URL parameters (GET), and are usually put there using a Javascript thing.
|
||||
*
|
||||
* This method will parse and validate those rules and create a "TransactionMatcher" which will attempt
|
||||
* to find transaction journals matching the users input. A maximum range of transactions to try (range) and
|
||||
* a maximum number of transactions to return (limit) are set as well.
|
||||
*
|
||||
* @param TestRuleFormRequest $request
|
||||
*
|
||||
* @return JsonResponse
|
||||
@@ -155,46 +137,46 @@ class SelectController extends Controller
|
||||
*/
|
||||
public function testTriggers(TestRuleFormRequest $request): JsonResponse
|
||||
{
|
||||
// build trigger array from response
|
||||
$triggers = $this->getValidTriggerList($request);
|
||||
// build fake rule
|
||||
$rule = new Rule;
|
||||
$triggers = new Collection;
|
||||
$rule->strict = '1' === $request->get('strict');
|
||||
|
||||
if (0 === count($triggers)) {
|
||||
// build trigger array from response
|
||||
$textTriggers = $this->getValidTriggerList($request);
|
||||
|
||||
// warn if nothing.
|
||||
if (0 === count($textTriggers)) {
|
||||
return response()->json(['html' => '', 'warning' => (string) trans('firefly.warning_no_valid_triggers')]); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$limit = (int) config('firefly.test-triggers.limit');
|
||||
$range = (int) config('firefly.test-triggers.range');
|
||||
$matchingTransactions = new Collection;
|
||||
$strict = '1' === $request->get('strict');
|
||||
/** @var TransactionMatcher $matcher */
|
||||
$matcher = app(TransactionMatcher::class);
|
||||
$matcher->setSearchLimit($range);
|
||||
$matcher->setTriggeredLimit($limit);
|
||||
$matcher->setTriggers($triggers);
|
||||
$matcher->setStrict($strict);
|
||||
try {
|
||||
$matchingTransactions = $matcher->findTransactionsByTriggers();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (FireflyException $exception) {
|
||||
Log::error(sprintf('Could not grab transactions in testTriggers(): %s', $exception->getMessage()));
|
||||
Log::error($exception->getTraceAsString());
|
||||
foreach ($textTriggers as $textTrigger) {
|
||||
$trigger = new RuleTrigger;
|
||||
$trigger->trigger_type = $textTrigger['type'];
|
||||
$trigger->trigger_value = $textTrigger['value'];
|
||||
$triggers->push($trigger);
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
$rule->ruleTriggers = $triggers;
|
||||
|
||||
// create new rule engine:
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
|
||||
// set rules:
|
||||
$newRuleEngine->setRules(new Collection([$rule]));
|
||||
$collection = $newRuleEngine->find();
|
||||
$collection = $collection->slice(0, 20);
|
||||
|
||||
// Warn the user if only a subset of transactions is returned
|
||||
$warning = '';
|
||||
if (count($matchingTransactions) === $limit) {
|
||||
$warning = (string) trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]); // @codeCoverageIgnore
|
||||
}
|
||||
if (0 === count($matchingTransactions)) {
|
||||
$warning = (string) trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]); // @codeCoverageIgnore
|
||||
if (0 === count($collection)) {
|
||||
$warning = (string) trans('firefly.warning_no_matching_transactions'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// Return json response
|
||||
$view = 'ERROR, see logs.';
|
||||
try {
|
||||
$view = view('list.journals-array-tiny', ['journals' => $matchingTransactions])->render();
|
||||
$view = view('list.journals-array-tiny', ['groups' => $collection])->render();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $exception) {
|
||||
Log::error(sprintf('Could not render view in testTriggers(): %s', $exception->getMessage()));
|
||||
@@ -211,10 +193,6 @@ class SelectController extends Controller
|
||||
* This method allows the user to test a certain set of rule triggers. The rule triggers are grabbed from
|
||||
* the rule itself.
|
||||
*
|
||||
* This method will parse and validate those rules and create a "TransactionMatcher" which will attempt
|
||||
* to find transaction journals matching the users input. A maximum range of transactions to try (range) and
|
||||
* a maximum number of transactions to return (limit) are set as well.
|
||||
*
|
||||
* @param Rule $rule
|
||||
*
|
||||
* @return JsonResponse
|
||||
@@ -228,37 +206,24 @@ class SelectController extends Controller
|
||||
return response()->json(['html' => '', 'warning' => (string) trans('firefly.warning_no_valid_triggers')]); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
$limit = (int) config('firefly.test-triggers.limit');
|
||||
$range = (int) config('firefly.test-triggers.range');
|
||||
$matchingTransactions = new Collection;
|
||||
|
||||
/** @var TransactionMatcher $matcher */
|
||||
$matcher = app(TransactionMatcher::class);
|
||||
$matcher->setTriggeredLimit($limit);
|
||||
$matcher->setSearchLimit($range);
|
||||
$matcher->setRule($rule);
|
||||
try {
|
||||
$matchingTransactions = $matcher->findTransactionsByRule();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (FireflyException $exception) {
|
||||
Log::error(sprintf('Could not grab transactions in testTriggersByRule(): %s', $exception->getMessage()));
|
||||
Log::error($exception->getTraceAsString());
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
// create new rule engine:
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
|
||||
// set rules:
|
||||
$newRuleEngine->setRules(new Collection([$rule]));
|
||||
$collection = $newRuleEngine->find();
|
||||
$collection = $collection->slice(0, 20);
|
||||
|
||||
// Warn the user if only a subset of transactions is returned
|
||||
$warning = '';
|
||||
if (count($matchingTransactions) === $limit) {
|
||||
$warning = (string) trans('firefly.warning_transaction_subset', ['max_num_transactions' => $limit]); // @codeCoverageIgnore
|
||||
}
|
||||
if (0 === count($matchingTransactions)) {
|
||||
$warning = (string) trans('firefly.warning_no_matching_transactions', ['num_transactions' => $range]); // @codeCoverageIgnore
|
||||
if (0 === count($collection)) {
|
||||
$warning = (string) trans('firefly.warning_no_matching_transactions'); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
// Return json response
|
||||
$view = 'ERROR, see logs.';
|
||||
try {
|
||||
$view = view('list.journals-array-tiny', ['journals' => $matchingTransactions])->render();
|
||||
$view = view('list.journals-array-tiny', ['groups' => $collection])->render();
|
||||
// @codeCoverageIgnoreStart
|
||||
} catch (Throwable $exception) {
|
||||
Log::error(sprintf('Could not render view in testTriggersByRule(): %s', $exception->getMessage()));
|
||||
|
||||
@@ -26,29 +26,22 @@ namespace FireflyIII\Http\Controllers\RuleGroup;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Exception;
|
||||
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\Http\Requests\SelectTransactionsRequest;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngine;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\View\View;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
* Class ExecutionController
|
||||
*/
|
||||
class ExecutionController extends Controller
|
||||
{
|
||||
/** @var AccountRepositoryInterface */
|
||||
private $repository;
|
||||
|
||||
/** @var RuleGroupRepositoryInterface */
|
||||
private $ruleGroupRepository;
|
||||
private RuleGroupRepositoryInterface $ruleGroupRepository;
|
||||
|
||||
/**
|
||||
* ExecutionController constructor.
|
||||
@@ -64,7 +57,6 @@ class ExecutionController extends Controller
|
||||
app('view')->share('title', (string) trans('firefly.rules'));
|
||||
app('view')->share('mainTitleIcon', 'fa-random');
|
||||
|
||||
$this->repository = app(AccountRepositoryInterface::class);
|
||||
$this->ruleGroupRepository = app(RuleGroupRepositoryInterface::class);
|
||||
|
||||
return $next($request);
|
||||
@@ -79,42 +71,30 @@ class ExecutionController extends Controller
|
||||
* @param SelectTransactionsRequest $request
|
||||
* @param RuleGroup $ruleGroup
|
||||
*
|
||||
* @throws Exception
|
||||
* @return RedirectResponse
|
||||
* @throws Exception
|
||||
*/
|
||||
public function execute(SelectTransactionsRequest $request, RuleGroup $ruleGroup): RedirectResponse
|
||||
{
|
||||
// Get parameters specified by the user
|
||||
$accounts = $this->repository->getAccountsById($request->get('accounts'));
|
||||
$startDate = new Carbon($request->get('start_date'));
|
||||
$endDate = new Carbon($request->get('end_date'));
|
||||
/** @var User $user */
|
||||
$user = auth()->user();
|
||||
$accounts = implode(',', $request->get('accounts'));
|
||||
$startDate = new Carbon($request->get('start'));
|
||||
$endDate = new Carbon($request->get('end'));
|
||||
$rules = $this->ruleGroupRepository->getActiveRules($ruleGroup);
|
||||
// create new rule engine:
|
||||
$newRuleEngine = app(RuleEngineInterface::class);
|
||||
$newRuleEngine->setUser($user);
|
||||
|
||||
// start looping.
|
||||
/** @var RuleEngine $ruleEngine */
|
||||
$ruleEngine = app(RuleEngine::class);
|
||||
$ruleEngine->setUser(auth()->user());
|
||||
// add extra operators:
|
||||
$newRuleEngine->addOperator(['type' => 'date_after', 'value' => $startDate->format('Y-m-d')]);
|
||||
$newRuleEngine->addOperator(['type' => 'date_before', 'value' => $endDate->format('Y-m-d')]);
|
||||
$newRuleEngine->addOperator(['type' => 'account_id', 'value' => $accounts]);
|
||||
|
||||
$rules = [];
|
||||
/** @var Rule $rule */
|
||||
foreach ($this->ruleGroupRepository->getActiveRules($ruleGroup) as $rule) {
|
||||
$rules[] = $rule->id;
|
||||
}
|
||||
|
||||
$ruleEngine->setRulesToApply($rules);
|
||||
$ruleEngine->setTriggerMode(RuleEngine::TRIGGER_STORE);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
$collector = app(GroupCollectorInterface::class);
|
||||
$collector->setAccounts($accounts);
|
||||
$collector->setRange($startDate, $endDate);
|
||||
$journals = $collector->getExtractedJournals();
|
||||
|
||||
/** @var array $journal */
|
||||
foreach ($journals as $journal) {
|
||||
Log::debug('Start of new journal.');
|
||||
$ruleEngine->processJournalArray($journal);
|
||||
Log::debug('Done with all rules for this group + done with journal.');
|
||||
}
|
||||
// set rules:
|
||||
$newRuleEngine->setRules($rules);
|
||||
$newRuleEngine->fire();
|
||||
|
||||
// Tell the user that the job is queued
|
||||
session()->flash('success', (string) trans('firefly.applied_rule_group_selection', ['title' => $ruleGroup->title]));
|
||||
|
||||
@@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
@@ -62,15 +63,33 @@ class SearchController extends Controller
|
||||
*/
|
||||
public function index(Request $request, SearchInterface $searcher)
|
||||
{
|
||||
$fullQuery = (string) $request->get('search');
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
// search params:
|
||||
$fullQuery = (string) $request->get('search');
|
||||
$page = 0 === (int) $request->get('page') ? 1 : (int) $request->get('page');
|
||||
$ruleId = (int) $request->get('rule');
|
||||
$rule = null;
|
||||
$ruleChanged = false;
|
||||
|
||||
// find rule, check if query is different, offer to update.
|
||||
$ruleRepository = app(RuleRepositoryInterface::class);
|
||||
$rule = $ruleRepository->find($ruleId);
|
||||
if (null !== $rule) {
|
||||
$originalQuery = $ruleRepository->getSearchQuery($rule);
|
||||
if ($originalQuery !== $fullQuery) {
|
||||
$ruleChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// parse search terms:
|
||||
$searcher->parseQuery($fullQuery);
|
||||
$query = $searcher->getWordsAsString();
|
||||
$modifiers = $searcher->getModifiers();
|
||||
$subTitle = (string) trans('breadcrumbs.search_result', ['query' => $query]);
|
||||
|
||||
return view('search.index', compact('query', 'modifiers', 'page', 'fullQuery', 'subTitle'));
|
||||
// words from query and operators:
|
||||
$query = $searcher->getWordsAsString();
|
||||
$operators = $searcher->getOperators();
|
||||
|
||||
$subTitle = (string) trans('breadcrumbs.search_result', ['query' => $fullQuery]);
|
||||
|
||||
return view('search.index', compact('query', 'operators', 'page', 'rule', 'fullQuery', 'subTitle', 'ruleId', 'ruleChanged'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,6 +113,7 @@ class SearchController extends Controller
|
||||
$parameters = ['search' => $fullQuery];
|
||||
$url = route('search.index') . '?' . http_build_query($parameters);
|
||||
$groups->setPath($url);
|
||||
|
||||
try {
|
||||
$html = view('search.search', compact('groups', 'hasPages', 'searchTime'))->render();
|
||||
// @codeCoverageIgnoreStart
|
||||
|
||||
@@ -180,8 +180,9 @@ class TagController extends Controller
|
||||
public function index(TagRepositoryInterface $repository)
|
||||
{
|
||||
// start with oldest tag
|
||||
$oldestTagDate = null === $repository->oldestTag() ? clone session('first') : $repository->oldestTag()->date;
|
||||
$newestTagDate = null === $repository->newestTag() ? new Carbon : $repository->newestTag()->date;
|
||||
$first = session('first', today()) ?? today();
|
||||
$oldestTagDate = null === $repository->oldestTag() ? clone $first : $repository->oldestTag()->date;
|
||||
$newestTagDate = null === $repository->newestTag() ? today() : $repository->newestTag()->date;
|
||||
$oldestTagDate->startOfYear();
|
||||
$newestTagDate->endOfYear();
|
||||
$tags = [];
|
||||
@@ -250,7 +251,7 @@ class TagController extends Controller
|
||||
);
|
||||
|
||||
$startPeriod = $this->repository->firstUseDate($tag);
|
||||
$startPeriod = $startPeriod ?? new Carbon;
|
||||
$startPeriod = $startPeriod ?? today(config('app.timezone'));
|
||||
$endPeriod = clone $end;
|
||||
$periods = $this->getTagPeriodOverview($tag, $startPeriod, $endPeriod);
|
||||
$path = route('tags.show', [$tag->id, $start->format('Y-m-d'), $end->format('Y-m-d')]);
|
||||
@@ -284,8 +285,8 @@ class TagController extends Controller
|
||||
$pageSize = (int) app('preferences')->get('listPageSize', 50)->data;
|
||||
$periods = [];
|
||||
$subTitle = (string) trans('firefly.all_journals_for_tag', ['tag' => $tag->tag]);
|
||||
$start = $this->repository->firstUseDate($tag) ?? new Carbon;
|
||||
$end = $this->repository->lastUseDate($tag) ?? new Carbon;
|
||||
$start = $this->repository->firstUseDate($tag) ?? today(config('app.timezone'));
|
||||
$end = $this->repository->lastUseDate($tag) ?? today(config('app.timezone'));
|
||||
$attachments = $this->repository->getAttachments($tag);
|
||||
$path = route('tags.show', [$tag->id, 'all']);
|
||||
$location = $this->repository->getLocation($tag);
|
||||
|
||||
@@ -54,8 +54,7 @@ class ConvertController extends Controller
|
||||
{
|
||||
use ModelInformation, UserNavigation;
|
||||
|
||||
/** @var JournalRepositoryInterface Journals and transactions overview */
|
||||
private $repository;
|
||||
private JournalRepositoryInterface $repository;
|
||||
|
||||
/**
|
||||
* ConvertController constructor.
|
||||
@@ -260,7 +259,7 @@ class ConvertController extends Controller
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$balance = app('steam')->balance($account);
|
||||
$balance = app('steam')->balance($account, today());
|
||||
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
$role = (string) $repository->getMetaValue($account, 'account_role');
|
||||
if ('' === $role) {
|
||||
@@ -289,7 +288,7 @@ class ConvertController extends Controller
|
||||
// group accounts:
|
||||
/** @var Account $account */
|
||||
foreach ($accountList as $account) {
|
||||
$balance = app('steam')->balance($account);
|
||||
$balance = app('steam')->balance($account, today());
|
||||
$currency = $repository->getAccountCurrency($account) ?? $defaultCurrency;
|
||||
$role = 'l_' . $account->accountType->type;
|
||||
$key = (string) trans('firefly.opt_group_' . $role);
|
||||
|
||||
@@ -71,8 +71,9 @@ class CreateController extends Controller
|
||||
app('preferences')->mark();
|
||||
|
||||
$title = $newGroup->title ?? $newGroup->transactionJournals->first()->description;
|
||||
|
||||
$link = route('transactions.show', [$newGroup->id]);
|
||||
session()->flash('success', trans('firefly.stored_journal', ['description' => $title]));
|
||||
session()->flash('success_uri', $link);
|
||||
|
||||
return redirect(route('transactions.show', [$newGroup->id]));
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ class IndexController extends Controller
|
||||
$first = $repository->firstNull();
|
||||
$start = null === $first ? new Carbon : $first->date;
|
||||
$last = $this->repository->getLast();
|
||||
$end = $last ? $last->date : new Carbon;
|
||||
$end = $last ? $last->date : today(config('app.timezone'));
|
||||
$subTitle = (string) trans('firefly.all_' . $objectType);
|
||||
|
||||
/** @var GroupCollectorInterface $collector */
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
* 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/>.
|
||||
*/
|
||||
/** @noinspection PhpDynamicAsStaticMethodCallInspection */
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
@@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,7 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
*/
|
||||
class NewUserFormRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
/**
|
||||
* Verify the request.
|
||||
*
|
||||
|
||||
@@ -144,7 +144,7 @@ class RecurrenceFormRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
$tomorrow = Carbon::now()->addDay();
|
||||
$rules = [
|
||||
// mandatory info for recurrence.
|
||||
|
||||
@@ -151,7 +151,7 @@ class ReportFormRequest extends FormRequest
|
||||
*/
|
||||
public function getEndDate(): Carbon
|
||||
{
|
||||
$date = new Carbon;
|
||||
$date = today(config('app.timezone'));
|
||||
$range = $this->get('daterange');
|
||||
$parts = explode(' - ', (string) $range);
|
||||
if (2 === count($parts)) {
|
||||
@@ -179,7 +179,7 @@ class ReportFormRequest extends FormRequest
|
||||
*/
|
||||
public function getStartDate(): Carbon
|
||||
{
|
||||
$date = new Carbon;
|
||||
$date = today(config('app.timezone'));
|
||||
$range = $this->get('daterange');
|
||||
$parts = explode(' - ', (string) $range);
|
||||
if (2 === count($parts)) {
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Support\Request\ConvertsDataTypes;
|
||||
use FireflyIII\Support\Request\GetRuleConfiguration;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
@@ -31,7 +32,7 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
*/
|
||||
class RuleFormRequest extends FormRequest
|
||||
{
|
||||
use ConvertsDataTypes;
|
||||
use ConvertsDataTypes, GetRuleConfiguration;
|
||||
/**
|
||||
* Verify the request.
|
||||
*
|
||||
@@ -73,14 +74,14 @@ class RuleFormRequest extends FormRequest
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
||||
$validTriggers = $this->getTriggers();
|
||||
$validActions = array_keys(config('firefly.rule-actions'));
|
||||
|
||||
// some actions require text (aka context):
|
||||
$contextActions = implode(',', config('firefly.context-rule-actions'));
|
||||
|
||||
// some triggers require text (aka context):
|
||||
$contextTriggers = implode(',', config('firefly.context-rule-triggers'));
|
||||
$contextTriggers = implode(',', $this->getTriggersWithContext());
|
||||
|
||||
// initial set of rules:
|
||||
$rules = [
|
||||
|
||||
@@ -57,8 +57,8 @@ class SelectTransactionsRequest extends FormRequest
|
||||
$today = Carbon::now()->addDay()->format('Y-m-d');
|
||||
|
||||
return [
|
||||
'start_date' => 'required|date|after:' . $first,
|
||||
'end_date' => 'required|date|before:' . $today,
|
||||
'start' => 'required|date|after:' . $first,
|
||||
'end' => 'required|date|before:' . $today,
|
||||
'accounts' => 'required',
|
||||
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
|
||||
];
|
||||
|
||||
@@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Requests;
|
||||
|
||||
use FireflyIII\Support\Request\GetRuleConfiguration;
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
/**
|
||||
@@ -31,6 +32,7 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
*/
|
||||
class TestRuleFormRequest extends FormRequest
|
||||
{
|
||||
use GetRuleConfiguration;
|
||||
/**
|
||||
* Verify the request.
|
||||
*
|
||||
@@ -51,7 +53,7 @@ class TestRuleFormRequest extends FormRequest
|
||||
public function rules(): array
|
||||
{
|
||||
// fixed
|
||||
$validTriggers = array_keys(config('firefly.rule-triggers'));
|
||||
$validTriggers = $this->getTriggers();
|
||||
$rules = [
|
||||
'rule-trigger.*' => 'required|min:1|in:' . implode(',', $validTriggers),
|
||||
'rule-trigger-value.*' => 'required|min:1|ruleTriggerValue',
|
||||
|
||||
@@ -151,7 +151,7 @@ class SubmitTelemetryData implements ShouldQueue
|
||||
{
|
||||
$telemetry->each(
|
||||
static function (Telemetry $entry) {
|
||||
$entry->submitted = new Carbon;
|
||||
$entry->submitted = today(config('app.timezone'));
|
||||
$entry->save();
|
||||
}
|
||||
);
|
||||
|
||||
69
app/Mail/NewIPAddressWarningMail.php
Normal file
69
app/Mail/NewIPAddressWarningMail.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
/*
|
||||
* NewIPAddressWarningMail.php
|
||||
* Copyright (c) 2020 james@firefly-iii.org
|
||||
*
|
||||
* This file is part of Firefly III (https://github.com/firefly-iii).
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
namespace FireflyIII\Mail;
|
||||
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Mail\Mailable;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Laravel\Passport\Client;
|
||||
|
||||
/**
|
||||
* Class NewIPAddressWarningMail
|
||||
*/
|
||||
class NewIPAddressWarningMail extends Mailable
|
||||
{
|
||||
use Queueable, SerializesModels;
|
||||
|
||||
public string $ipAddress;
|
||||
public string $time;
|
||||
public string $host;
|
||||
/**
|
||||
* OAuthTokenCreatedMail constructor.
|
||||
*
|
||||
* @param string $ipAddress
|
||||
*/
|
||||
public function __construct(string $ipAddress)
|
||||
{
|
||||
$this->ipAddress = $ipAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the message.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function build(): self
|
||||
{
|
||||
// time
|
||||
$this->time = now()->formatLocalized((string)trans('config.date_time'));
|
||||
$this->host = '';
|
||||
$host = gethostbyaddr($this->ipAddress);
|
||||
if($host !== $this->ipAddress) {
|
||||
$this->host = $host;
|
||||
}
|
||||
|
||||
return $this->view('emails.new-ip-html')->text('emails.new-ip-text')
|
||||
->subject((string) trans('email.login_from_new_ip'));
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ namespace FireflyIII\Providers;
|
||||
|
||||
use Exception;
|
||||
use FireflyIII\Events\AdminRequestedTestMessage;
|
||||
use FireflyIII\Events\DetectedNewIPAddress;
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\RequestedReportOnJournals;
|
||||
@@ -67,6 +68,10 @@ class EventServiceProvider extends ServiceProvider
|
||||
Login::class => [
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@checkSingleUserIsAdmin',
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@demoUserBackToEnglish',
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@storeUserIPAddress',
|
||||
],
|
||||
DetectedNewIPAddress::class => [
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@notifyNewIPAddress',
|
||||
],
|
||||
RequestedVersionCheckStatus::class => [
|
||||
'FireflyIII\Handlers\Events\VersionCheckEventHandler@checkForUpdates',
|
||||
|
||||
@@ -22,7 +22,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Generator\Chart\Basic\ChartJsGenerator;
|
||||
use FireflyIII\Generator\Chart\Basic\GeneratorInterface;
|
||||
use FireflyIII\Helpers\Attachments\AttachmentHelper;
|
||||
@@ -45,11 +44,8 @@ use FireflyIII\Repositories\TransactionType\TransactionTypeRepository;
|
||||
use FireflyIII\Repositories\TransactionType\TransactionTypeRepositoryInterface;
|
||||
use FireflyIII\Repositories\User\UserRepository;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\Services\Currency\ExchangeRateInterface;
|
||||
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequest;
|
||||
use FireflyIII\Services\FireflyIIIOrg\Update\UpdateRequestInterface;
|
||||
use FireflyIII\Services\IP\IpifyOrg;
|
||||
use FireflyIII\Services\IP\IPRetrievalInterface;
|
||||
use FireflyIII\Services\Password\PwndVerifierV2;
|
||||
use FireflyIII\Services\Password\Verifier;
|
||||
use FireflyIII\Support\Amount;
|
||||
@@ -63,6 +59,8 @@ use FireflyIII\Support\Navigation;
|
||||
use FireflyIII\Support\Preferences;
|
||||
use FireflyIII\Support\Steam;
|
||||
use FireflyIII\Support\Telemetry;
|
||||
use FireflyIII\TransactionRules\Engine\RuleEngineInterface;
|
||||
use FireflyIII\TransactionRules\Engine\SearchRuleEngine;
|
||||
use FireflyIII\Validation\FireflyValidator;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
@@ -192,6 +190,19 @@ class FireflyServiceProvider extends ServiceProvider
|
||||
}
|
||||
);
|
||||
|
||||
$this->app->bind(
|
||||
RuleEngineInterface::class,
|
||||
static function (Application $app) {
|
||||
/** @var SearchRuleEngine $engine */
|
||||
$engine = app(SearchRuleEngine::class);
|
||||
if ($app->auth->check()) {
|
||||
$engine->setUser(auth()->user());
|
||||
}
|
||||
|
||||
return $engine;
|
||||
}
|
||||
);
|
||||
|
||||
// more generators:
|
||||
$this->app->bind(PopupReportInterface::class, PopupReport::class);
|
||||
$this->app->bind(HelpInterface::class, Help::class);
|
||||
@@ -200,18 +211,9 @@ class FireflyServiceProvider extends ServiceProvider
|
||||
$this->app->bind(UpdateRequestInterface::class, UpdateRequest::class);
|
||||
$this->app->bind(TelemetryRepositoryInterface::class, TelemetryRepository::class);
|
||||
|
||||
$class = (string) config(sprintf('firefly.cer_providers.%s', (string) config('firefly.cer_provider')));
|
||||
if ('' === $class) {
|
||||
throw new FireflyException('Invalid currency exchange rate provider. Cannot continue.');
|
||||
}
|
||||
$this->app->bind(ExchangeRateInterface::class, $class);
|
||||
|
||||
// password verifier thing
|
||||
$this->app->bind(Verifier::class, PwndVerifierV2::class);
|
||||
|
||||
// IP thing:
|
||||
$this->app->bind(IPRetrievalInterface::class, IpifyOrg::class);
|
||||
|
||||
// net worth thing.
|
||||
$this->app->bind(NetWorthInterface::class, NetWorth::class);
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Providers;
|
||||
|
||||
use FireflyIII\Support\Search\BetterQuerySearch;
|
||||
use FireflyIII\Support\Search\OperatorQuerySearch;
|
||||
use FireflyIII\Support\Search\SearchInterface;
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
@@ -48,8 +48,8 @@ class SearchServiceProvider extends ServiceProvider
|
||||
$this->app->bind(
|
||||
SearchInterface::class,
|
||||
function (Application $app) {
|
||||
/** @var BetterQuerySearch $search */
|
||||
$search = app(BetterQuerySearch::class);
|
||||
/** @var OperatorQuerySearch $search */
|
||||
$search = app(OperatorQuerySearch::class);
|
||||
if ($app->auth->check()) {
|
||||
$search->setUser(auth()->user());
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ use FireflyIII\Services\Internal\Destroy\AccountDestroyService;
|
||||
use FireflyIII\Services\Internal\Update\AccountUpdateService;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use \Illuminate\Database\Eloquent\Builder as EloquentBuilder;
|
||||
use Illuminate\Support\Collection;
|
||||
use Log;
|
||||
use Storage;
|
||||
@@ -346,7 +347,7 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
return null;
|
||||
}
|
||||
if (1 === $result->count()) {
|
||||
return (string)$result->first()->data;
|
||||
return (string) $result->first()->data;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -704,4 +705,38 @@ class AccountRepository implements AccountRepositoryInterface
|
||||
$account->save();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
public function searchAccountNr(string $query, array $types, int $limit): Collection
|
||||
{
|
||||
$dbQuery = $this->user->accounts()->distinct()
|
||||
->leftJoin('account_meta', 'accounts.id', 'account_meta.account_id')
|
||||
->where('accounts.active', 1)
|
||||
->orderBy('accounts.order', 'ASC')
|
||||
->orderBy('accounts.account_type_id', 'ASC')
|
||||
->orderBy('accounts.name', 'ASC')
|
||||
->with(['accountType', 'accountMeta']);
|
||||
if ('' !== $query) {
|
||||
// split query on spaces just in case:
|
||||
$parts = explode(' ', $query);
|
||||
foreach ($parts as $part) {
|
||||
$search = sprintf('%%%s%%', $part);
|
||||
$dbQuery->where(function (EloquentBuilder $q1) use ($search) {
|
||||
$q1->where('accounts.iban', 'LIKE', $search);
|
||||
$q1->orWhere(function (EloquentBuilder $q2) use ($search) {
|
||||
$q2->where('account_meta.name', '=', 'account_number');
|
||||
$q2->where('account_meta.data', 'LIKE', $search);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
if (count($types) > 0) {
|
||||
$dbQuery->leftJoin('account_types', 'accounts.account_type_id', '=', 'account_types.id');
|
||||
$dbQuery->whereIn('account_types.type', $types);
|
||||
}
|
||||
|
||||
return $dbQuery->take($limit)->get(['accounts.*']);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,6 +289,15 @@ interface AccountRepositoryInterface
|
||||
*/
|
||||
public function searchAccount(string $query, array $types, int $limit): Collection;
|
||||
|
||||
/**
|
||||
* @param string $query
|
||||
* @param array $types
|
||||
* @param int $limit
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function searchAccountNr(string $query, array $types, int $limit): Collection;
|
||||
|
||||
/**
|
||||
* @param User $user
|
||||
*/
|
||||
|
||||
@@ -39,19 +39,7 @@ use Log;
|
||||
*/
|
||||
class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
die(get_class($this));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* @param AvailableBudget $availableBudget
|
||||
@@ -78,8 +66,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
{
|
||||
return $this->user->availableBudgets()
|
||||
->where('transaction_currency_id', $currency->id)
|
||||
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 00:00:00'))
|
||||
->where('start_date', $start->format('Y-m-d'))
|
||||
->where('end_date', $end->format('Y-m-d'))
|
||||
->first();
|
||||
|
||||
}
|
||||
@@ -98,8 +86,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
if (null !== $start && null !== $end) {
|
||||
$query->where(
|
||||
static function (Builder $q1) use ($start, $end) {
|
||||
$q1->where('start_date', '=', $start->format('Y-m-d 00:00:00'));
|
||||
$q1->where('end_date', '=', $end->format('Y-m-d 00:00:00'));
|
||||
$q1->where('start_date', '=', $start->format('Y-m-d'));
|
||||
$q1->where('end_date', '=', $end->format('Y-m-d'));
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -119,8 +107,8 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
$amount = '0';
|
||||
$availableBudget = $this->user->availableBudgets()
|
||||
->where('transaction_currency_id', $currency->id)
|
||||
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 00:00:00'))->first();
|
||||
->where('start_date', $start->format('Y-m-d'))
|
||||
->where('end_date', $end->format('Y-m-d'))->first();
|
||||
if (null !== $availableBudget) {
|
||||
$amount = (string)$availableBudget->amount;
|
||||
}
|
||||
@@ -174,10 +162,10 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
$query = $this->user->availableBudgets();
|
||||
|
||||
if (null !== $start) {
|
||||
$query->where('start_date', '>=', $start->format('Y-m-d H:i:s'));
|
||||
$query->where('start_date', '>=', $start->format('Y-m-d'));
|
||||
}
|
||||
if (null !== $end) {
|
||||
$query->where('end_date', '<=', $end->format('Y-m-d H:i:s'));
|
||||
$query->where('end_date', '<=', $end->format('Y-m-d'));
|
||||
}
|
||||
|
||||
return $query->get();
|
||||
@@ -196,14 +184,14 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
{
|
||||
$availableBudget = $this->user->availableBudgets()
|
||||
->where('transaction_currency_id', $currency->id)
|
||||
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 00:00:00'))->first();
|
||||
->where('start_date', $start->format('Y-m-d'))
|
||||
->where('end_date', $end->format('Y-m-d'))->first();
|
||||
if (null === $availableBudget) {
|
||||
$availableBudget = new AvailableBudget;
|
||||
$availableBudget->user()->associate($this->user);
|
||||
$availableBudget->transactionCurrency()->associate($currency);
|
||||
$availableBudget->start_date = $start->format('Y-m-d 00:00:00');
|
||||
$availableBudget->end_date = $end->format('Y-m-d 00:00:00');
|
||||
$availableBudget->start_date = $start->format('Y-m-d');
|
||||
$availableBudget->end_date = $end->format('Y-m-d');
|
||||
}
|
||||
$availableBudget->amount = $amount;
|
||||
$availableBudget->save();
|
||||
@@ -226,13 +214,21 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
*/
|
||||
public function store(array $data): ?AvailableBudget
|
||||
{
|
||||
$start = $data['start'];
|
||||
if($start instanceof Carbon) {
|
||||
$start = $data['start']->startOfDay();
|
||||
}
|
||||
$end = $data['end'];
|
||||
if($end instanceof Carbon) {
|
||||
$end = $data['end']->endOfDay();
|
||||
}
|
||||
return AvailableBudget::create(
|
||||
[
|
||||
'user_id' => $this->user->id,
|
||||
'transaction_currency_id' => $data['currency']->id,
|
||||
'amount' => $data['amount'],
|
||||
'start_date' => $data['start'],
|
||||
'end_date' => $data['end'],
|
||||
'start_date' => $start,
|
||||
'end_date' => $end,
|
||||
|
||||
]
|
||||
);
|
||||
@@ -265,17 +261,27 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
{
|
||||
$existing = $this->user->availableBudgets()
|
||||
->where('transaction_currency_id', $data['currency_id'])
|
||||
->where('start_date', $data['start']->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $data['end']->format('Y-m-d 00:00:00'))
|
||||
->where('start_date', $data['start']->format('Y-m-d'))
|
||||
->where('end_date', $data['end']->format('Y-m-d'))
|
||||
->where('id', '!=', $availableBudget->id)
|
||||
->first();
|
||||
|
||||
if (null !== $existing) {
|
||||
throw new FireflyException(sprintf('An entry already exists for these parameters: available budget object with ID #%d', $existing->id));
|
||||
}
|
||||
|
||||
$start = $data['start'];
|
||||
if($start instanceof Carbon) {
|
||||
$start = $data['start']->startOfDay();
|
||||
}
|
||||
$end = $data['end'];
|
||||
if($end instanceof Carbon) {
|
||||
$end = $data['end']->endOfDay();
|
||||
}
|
||||
|
||||
$availableBudget->transaction_currency_id = $data['currency_id'];
|
||||
$availableBudget->start_date = $data['start'];
|
||||
$availableBudget->end_date = $data['end'];
|
||||
$availableBudget->start_date = $start;
|
||||
$availableBudget->end_date = $end;
|
||||
$availableBudget->amount = $data['amount'];
|
||||
$availableBudget->save();
|
||||
|
||||
@@ -299,7 +305,7 @@ class AvailableBudgetRepository implements AvailableBudgetRepositoryInterface
|
||||
return $this->user
|
||||
->availableBudgets()
|
||||
->where('transaction_currency_id', $currency->id)
|
||||
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 00:00:00'))->first();
|
||||
->where('start_date', $start->format('Y-m-d'))
|
||||
->where('end_date', $end->format('Y-m-d'))->first();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,17 +44,6 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
{
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
die(get_class($this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells you which amount has been budgeted (for the given budgets)
|
||||
* in the selected query. Returns a positive amount as a string.
|
||||
@@ -115,8 +104,8 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
{
|
||||
return $budget->budgetlimits()
|
||||
->where('transaction_currency_id', $currency->id)
|
||||
->where('start_date', $start->format('Y-m-d'))
|
||||
->where('end_date', $end->format('Y-m-d'))->first();
|
||||
->where('start_date', $start->format('Y-m-d 00:00:00'))
|
||||
->where('end_date', $end->format('Y-m-d 23:59:59'))->first();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -307,7 +296,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
// find limit with same date range and currency.
|
||||
$limit = $budget->budgetlimits()
|
||||
->where('budget_limits.start_date', $data['start_date']->format('Y-m-d 00:00:00'))
|
||||
->where('budget_limits.end_date', $data['end_date']->format('Y-m-d 00:00:00'))
|
||||
->where('budget_limits.end_date', $data['end_date']->format('Y-m-d 23:59:59'))
|
||||
->where('budget_limits.transaction_currency_id', $currency->id)
|
||||
->get(['budget_limits.*'])->first();
|
||||
if (null !== $limit) {
|
||||
@@ -319,7 +308,7 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
$limit = new BudgetLimit;
|
||||
$limit->budget()->associate($budget);
|
||||
$limit->start_date = $data['start_date']->format('Y-m-d 00:00:00');
|
||||
$limit->end_date = $data['end_date']->format('Y-m-d 00:00:00');
|
||||
$limit->end_date = $data['end_date']->format('Y-m-d 23:59:59');
|
||||
$limit->amount = $data['amount'];
|
||||
$limit->transaction_currency_id = $currency->id;
|
||||
$limit->save();
|
||||
@@ -337,9 +326,9 @@ class BudgetLimitRepository implements BudgetLimitRepositoryInterface
|
||||
public function update(BudgetLimit $budgetLimit, array $data): BudgetLimit
|
||||
{
|
||||
$budgetLimit->amount = array_key_exists('amount',$data) ? $data['amount'] : $budgetLimit->amount;
|
||||
$budgetLimit->budget_id = array_key_exists('budget_id', $data) ? $data['budget_id'] : $budgetLimit->id;
|
||||
$budgetLimit->budget_id = array_key_exists('budget_id', $data) ? $data['budget_id'] : $budgetLimit->budget_id;
|
||||
$budgetLimit->start_date = array_key_exists('start_date', $data) ? $data['start_date']->format('Y-m-d 00:00:00') : $budgetLimit->start_date;
|
||||
$budgetLimit->end_date = array_key_exists('end_date', $data) ? $data['end_date']->format('Y-m-d 00:00:00') : $budgetLimit->end_date;
|
||||
$budgetLimit->end_date = array_key_exists('end_date', $data) ? $data['end_date']->format('Y-m-d 23:59:59') : $budgetLimit->end_date;
|
||||
|
||||
// if no currency has been provided, use the user's default currency:
|
||||
$currency = null;
|
||||
|
||||
@@ -307,7 +307,7 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
$autoBudget->save();
|
||||
|
||||
// create initial budget limit.
|
||||
$today = new Carbon;
|
||||
$today = today(config('app.timezone'));
|
||||
$start = app('navigation')->startOfPeriod($today, $autoBudget->period);
|
||||
$end = app('navigation')->endOfPeriod($start, $autoBudget->period);
|
||||
|
||||
@@ -317,8 +317,8 @@ class BudgetRepository implements BudgetRepositoryInterface
|
||||
[
|
||||
'budget_id' => $newBudget->id,
|
||||
'transaction_currency_id' => $autoBudget->transaction_currency_id,
|
||||
'start_date' => $start->format('Y-m-d'),
|
||||
'end_date' => $end->format('Y-m-d'),
|
||||
'start_date' => $start,
|
||||
'end_date' => $end,
|
||||
'amount' => $autoBudget->amount,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -41,19 +41,7 @@ use Log;
|
||||
*/
|
||||
class OperationsRepository implements OperationsRepositoryInterface
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
die(get_class($this));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* A method that returns the amount of money budgeted per day for this budget,
|
||||
|
||||
@@ -47,18 +47,7 @@ use Log;
|
||||
*/
|
||||
class CurrencyRepository implements CurrencyRepositoryInterface
|
||||
{
|
||||
/** @var User */
|
||||
private $user;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
if ('testing' === config('app.env')) {
|
||||
Log::warning(sprintf('%s should not be instantiated in the TEST environment!', get_class($this)));
|
||||
}
|
||||
}
|
||||
private User $user;
|
||||
|
||||
/**
|
||||
* @param TransactionCurrency $currency
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user