Merge branch 'develop' of https://github.com/JC5/firefly-iii into develop

# By Robert Horlings
# Via Robert Horlings (2) and James Cole (1)
* 'develop' of https://github.com/JC5/firefly-iii:
  Added button to specify parameters and execute the job
  Added missing imports for job
  Implemented job to execute rulegroup on existing transactions
  Moved JournalCollector to other package to encourage reuse
This commit is contained in:
James Cole
2016-02-23 15:47:39 +01:00
11 changed files with 327 additions and 4 deletions

View File

@@ -88,7 +88,7 @@ class Processor
public function collectJournals()
{
$args = [$this->accounts, Auth::user(), $this->settings['startDate'], $this->settings['endDate']];
$journalCollector = app('FireflyIII\Export\JournalCollector', $args);
$journalCollector = app('FireflyIII\Repositories\Journal\JournalCollector', $args);
$this->journals = $journalCollector->collect();
Log::debug(
'Collected ' .

View File

@@ -4,10 +4,14 @@ declare(strict_types = 1);
namespace FireflyIII\Http\Controllers;
use Auth;
use Carbon\Carbon;
use ExpandedForm;
use FireflyIII\Http\Requests\RuleGroupFormRequest;
use FireflyIII\Http\Requests\SelectTransactionsRequest;
use FireflyIII\Jobs\ExecuteRuleGroupOnExistingTransactions;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use Input;
use Preferences;
use Session;
@@ -205,4 +209,47 @@ class RuleGroupController extends Controller
}
/**
* Shows a form for the user to select a range of transactions to execute this rulegroup for
* @param RuleGroup $ruleGroup
*/
public function selectTransactions(AccountRepositoryInterface $repository, RuleGroup $ruleGroup)
{
// does the user have shared accounts?
$accounts = $repository->getAccounts(['Default account', 'Asset account']);
$accountList = ExpandedForm::makeSelectList($accounts);
$checkedAccounts = array_keys($accountList);
$first = session('first')->format('Y-m-d');
$today = Carbon::create()->format('Y-m-d');
return view('rules.rule-group.select-transactions', compact('checkedAccounts', 'accountList', 'first', 'today', 'ruleGroup'));
}
/**
* Execute the given rulegroup on a set of existing transactions
* @param RuleGroup $ruleGroup
*/
public function execute(SelectTransactionsRequest $request, AccountRepositoryInterface $repository, RuleGroup $ruleGroup)
{
// Get parameters specified by the user
$accounts = $repository->get($request->get('accounts'));
$startDate = new Carbon($request->get('start_date'));
$endDate = new Carbon($request->get('end_date'));
// Create a job to do the work asynchronously
$job = new ExecuteRuleGroupOnExistingTransactions($ruleGroup);
// Apply parameters to the job
$job->setUser(Auth::user());
$job->setAccounts($accounts);
$job->setStartDate($startDate);
$job->setEndDate($endDate);
// Dispatch a new job to execute it in a queue
$this->dispatch($job);
// Tell the user that the job is queued
Session::flash('success', trans('firefly.executed_group_on_existing_transactions', ['title' => $ruleGroup->title]));
return redirect()->route('rules.index');
}
}

View File

@@ -0,0 +1,50 @@
<?php
declare(strict_types = 1);
/**
* SelectTransactionsRequest.php
* Copyright (C) 2016 Robert Horlings
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Http\Requests;
use Auth;
use Carbon\Carbon;
/**
* Class ExportFormRequest
*
*
* @package FireflyIII\Http\Requests
*/
class SelectTransactionsRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return Auth::check();
}
/**
* @return array
*/
public function rules()
{
$sessionFirst = clone session('first');
$first = $sessionFirst->subDay()->format('Y-m-d');
$today = Carbon::create()->addDay()->format('Y-m-d');
return [
'start_date' => 'required|date|after:' . $first,
'end_date' => 'required|date|before:' . $today,
'accounts' => 'required',
'accounts.*' => 'required|exists:accounts,id|belongsToUser:accounts',
];
}
}

View File

@@ -289,12 +289,14 @@ Route::group(
Route::get('/rules/groups/delete/{ruleGroup}', ['uses' => 'RuleGroupController@delete', 'as' => 'rules.rule-group.delete']);
Route::get('/rules/groups/up/{ruleGroup}', ['uses' => 'RuleGroupController@up', 'as' => 'rules.rule-group.up']);
Route::get('/rules/groups/down/{ruleGroup}', ['uses' => 'RuleGroupController@down', 'as' => 'rules.rule-group.down']);
Route::get('/rules/groups/select_transactions/{ruleGroup}', ['uses' => 'RuleGroupController@selectTransactions', 'as' => 'rules.rule-group.select_transactions']);
// rule groups POST
Route::post('/rules/groups/store', ['uses' => 'RuleGroupController@store', 'as' => 'rules.rule-group.store']);
Route::post('/rules/groups/update/{ruleGroup}', ['uses' => 'RuleGroupController@update', 'as' => 'rules.rule-group.update']);
Route::post('/rules/groups/destroy/{ruleGroup}', ['uses' => 'RuleGroupController@destroy', 'as' => 'rules.rule-group.destroy']);
Route::post('/rules/groups/execute/{ruleGroup}', ['uses' => 'RuleGroupController@execute', 'as' => 'rules.rule-group.execute']);
/**
* Search Controller
*/

View File

@@ -0,0 +1,158 @@
<?php
namespace FireflyIII\Jobs;
use Carbon\Carbon;
use FireflyIII\Jobs\Job;
use FireflyIII\User;
use FireflyIII\Models\RuleGroup;
use FireflyIII\Rules\Processor;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Log;
class ExecuteRuleGroupOnExistingTransactions extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
/** @var RuleGroup */
private $ruleGroup;
/** @var Collection */
private $accounts;
/** @var Carbon */
private $endDate;
/** @var Carbon */
private $startDate;
/** @var User */
private $user;
/**
* Create a new job instance.
*
* @return void
*/
public function __construct(RuleGroup $ruleGroup)
{
$this->ruleGroup = $ruleGroup;
}
/**
* Execute the job.
*
* @return void
*/
public function handle()
{
// Lookup all journals that match the parameters specified
$journals = $this->collectJournals();
// Find processors for each rule within the current rule group
$processors = $this->collectProcessors();
// Execute the rules for each transaction
foreach($journals as $journal) {
/** @var Processor $processor */
foreach ($processors as $processor) {
$processor->handleTransactionJournal($journal);
// Stop processing this group if the rule specifies 'stop_processing'
if ($processor->getRule()->stop_processing) {
break;
}
}
}
}
/**
* Collect all journals that should be processed
* @return Collection
*/
protected function collectJournals()
{
$args = [$this->accounts, $this->user, $this->startDate, $this->endDate];
$journalCollector = app('FireflyIII\Repositories\Journal\JournalCollector', $args);
return $journalCollector->collect();
}
/**
* Collects a list of rule processors, one for each rule within the rule group
* @return array
*/
protected function collectProcessors() {
// Find all rules belonging to this rulegroup
$rules = $this->ruleGroup->rules()
->leftJoin('rule_triggers', 'rules.id', '=', 'rule_triggers.rule_id')
->where('rule_triggers.trigger_type', 'user_action')
->where('rule_triggers.trigger_value', 'store-journal')
->where('rules.active', 1)
->get(['rules.*']);
// Create a list of processors for these rules
return array_map( function( $rule ) {
return Processor::make($rule);
}, $rules->all());
}
/**
* @return User
*/
public function getUser() {
return $this->user;
}
/**
*
* @param User $user
*/
public function setUser(User $user) {
$this->user = $user;
}
/**
* @return Collection
*/
public function getAccounts() {
return $this->accounts;
}
/**
*
* @param Carbon $user
*/
public function setAccounts(Collection $accounts) {
$this->accounts = $accounts;
}
/**
* @return \Carbon\Carbon
*/
public function getStartDate() {
return $this->startDate;
}
/**
*
* @param Carbon $date
*/
public function setStartDate(Carbon $date) {
$this->startDate = $date;
}
/**
* @return \Carbon\Carbon
*/
public function getEndDate() {
return $this->endDate;
}
/**
*
* @param Carbon $date
*/
public function setEndDate(Carbon $date) {
$this->endDate = $date;
}
}

View File

@@ -8,7 +8,7 @@ declare(strict_types = 1);
* of the MIT license. See the LICENSE file for details.
*/
namespace FireflyIII\Export;
namespace FireflyIII\Repositories\Journal;
use Carbon\Carbon;
use FireflyIII\User;

View File

@@ -140,6 +140,14 @@ final class Processor
}
/**
*
* @return \FireflyIII\Models\Rule
*/
public function getRule() {
return $this->rule;
}
/**
* @return bool
*/

View File

@@ -133,6 +133,13 @@ return [
'warning_no_matching_transactions' => 'No matching transactions found. Please note that for performance reasons, only the last :num_transactions transactions have been checked.',
'warning_no_valid_triggers' => 'No valid triggers provided.',
'execute_on_existing_transactions' => 'Execute for existing transactions',
'execute_on_existing_transactions_intro' => 'When a rule or group has been changed or added, you can execute it for existing transactions',
'execute_on_existing_transactions_short' => 'Existing transactions',
'executed_group_on_existing_transactions' => 'Executed group ":title" for existing transactions',
'include_transactions_from_accounts' => 'Include transactions from these accounts',
'execute' => 'Execute',
// actions and triggers
'rule_trigger_user_action' => 'User action is ":trigger_value"',
'rule_trigger_from_account_starts' => 'Source account starts with ":trigger_value"',

View File

@@ -131,6 +131,13 @@ return [
'warning_transaction_subset' => 'Je ziet hier maximaal :max_num_transactions transacties omdat je anders veel te lang moet wachten',
'warning_no_matching_transactions' => 'Niks gevonden in je laatste :num_transactions transacties.',
'warning_no_valid_triggers' => 'Geen geldige triggers gevonden.',
'execute_on_existing_transactions' => 'Uitvoeren op bestaande transacties',
'execute_on_existing_transactions_intro' => 'Wanneer een regel of groep is veranderd of toegevoegd, kun je hem hier uitvoeren voor bestaande transacties.',
'execute_on_existing_transactions_short' => 'Bestaande transacties',
'executed_group_on_existing_transactions' => 'Groep :title is uitgevoerd op bestaande transacties',
'include_transactions_from_accounts' => 'Gebruik transacties van deze rekeningen',
'execute' => 'Uitvoeren',
// actions and triggers
'rule_trigger_user_action' => 'Gebruikersactie is ":trigger_value"',

View File

@@ -47,6 +47,8 @@
class="fa fa-fw fa-pencil"></i> {{ 'edit'|_ }}</a></li>
<li><a href="{{ route('rules.rule-group.delete',ruleGroup.id) }}"><i
class="fa fa-fw fa-trash"></i> {{ 'delete'|_ }}</a></li>
<li><a href="{{ route('rules.rule-group.select_transactions',ruleGroup.id) }}"><i
class="fa fa-fw fa-anchor"></i> {{ 'execute_on_existing_transactions_short'|_ }}</a></li>
{% if ruleGroup.order > 1 %}
<li><a href="{{ route('rules.rule-group.up',ruleGroup.id) }}"><i
class="fa fa-fw fa-arrow-up"></i> {{ 'move_rule_group_up'|_ }}</a></li>

View File

@@ -0,0 +1,42 @@
{% extends "./layout/default.twig" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('rules.rule-group.execute', ruleGroup.id) }}" accept-charset="UTF-8" class="form-horizontal" id="execute-rule-group">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-12 col-sm-12 col-xs-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ 'execute_on_existing_transactions'|_ }}</h3>
</div>
<div class="box-body">
<div id="form-body">
<p>
{{ 'execute_on_existing_transactions_intro'|_ }}
</p>
<div class="row">
<div class="col-lg-6 col-md-8 col-sm-12 col-xs-12">
{{ ExpandedForm.date('start_date', first) }}
{{ ExpandedForm.date('end_date', today) }}
<!-- ACCOUNTS -->
{{ ExpandedForm.multiCheckbox('accounts',accountList, checkedAccounts, {' class': 'account-checkbox', 'label': trans('firefly.include_transactions_from_accounts') }) }}
</div>
</div>
</div>
</div>
<div class="box-footer">
<input type="submit" name="submit" value="{{ 'execute'|_ }}" id="do-execute-button" class="btn btn-success pull-right"/>
</div>
</div>
</div>
</div>
</form>
{% endblock %}