From 8f0e36a8e4e545ca7bbb4e81905c18b498c22019 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 8 Apr 2018 16:27:52 +0200 Subject: [PATCH] Code for #1324 --- app/Factory/BillFactory.php | 22 +- app/Http/Controllers/BillController.php | 17 +- .../Json/AutoCompleteController.php | 14 + app/Http/Controllers/RuleController.php | 287 +++++++++++------- app/Http/Requests/BillFormRequest.php | 36 +-- app/Models/Bill.php | 16 +- .../Internal/Update/BillUpdateService.php | 23 +- app/TransactionRules/Triggers/CurrencyIs.php | 92 ++++++ config/firefly.php | 5 +- config/upgrade.php | 1 + .../2018_04_07_210913_changes_for_v473.php | 4 +- public/js/ff/rules/create-edit.js | 3 + resources/lang/en_US/firefly.php | 2 + resources/lang/en_US/form.php | 2 + resources/views/bills/create.twig | 2 +- resources/views/bills/edit.twig | 7 +- resources/views/rules/rule/create.twig | 6 +- routes/web.php | 3 +- 18 files changed, 377 insertions(+), 165 deletions(-) create mode 100644 app/TransactionRules/Triggers/CurrencyIs.php diff --git a/app/Factory/BillFactory.php b/app/Factory/BillFactory.php index e8522f6842..28b4a3544d 100644 --- a/app/Factory/BillFactory.php +++ b/app/Factory/BillFactory.php @@ -48,17 +48,17 @@ class BillFactory /** @var Bill $bill */ $bill = Bill::create( [ - 'name' => $data['name'], - 'match' => 'MIGRATED_TO_RULES', - 'amount_min' => $data['amount_min'], - 'user_id' => $this->user->id, - 'currency_id' => $data['currency_id'], - 'amount_max' => $data['amount_max'], - 'date' => $data['date'], - 'repeat_freq' => $data['repeat_freq'], - 'skip' => $data['skip'], - 'automatch' => true, - 'active' => $data['active'], + 'name' => $data['name'], + 'match' => 'MIGRATED_TO_RULES', + 'amount_min' => $data['amount_min'], + 'user_id' => $this->user->id, + 'transaction_currency_id' => $data['transaction_currency_id'], + 'amount_max' => $data['amount_max'], + 'date' => $data['date'], + 'repeat_freq' => $data['repeat_freq'], + 'skip' => $data['skip'], + 'automatch' => true, + 'active' => $data['active'], ] ); diff --git a/app/Http/Controllers/BillController.php b/app/Http/Controllers/BillController.php index 45e4a6293d..29b4770bdb 100644 --- a/app/Http/Controllers/BillController.php +++ b/app/Http/Controllers/BillController.php @@ -136,7 +136,7 @@ class BillController extends Controller * * @return View */ - public function edit(Request $request, Bill $bill) + public function edit(Request $request, CurrencyRepositoryInterface $repository, Bill $bill) { $periods = []; foreach (config('firefly.bill_periods') as $current) { @@ -152,6 +152,8 @@ class BillController extends Controller $currency = app('amount')->getDefaultCurrency(); $bill->amount_min = round($bill->amount_min, $currency->decimal_places); $bill->amount_max = round($bill->amount_max, $currency->decimal_places); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $currencies = ExpandedForm::makeSelectList($repository->get()); $preFilled = [ 'notes' => '', @@ -167,7 +169,7 @@ class BillController extends Controller $request->session()->forget('bills.edit.fromUpdate'); - return view('bills.edit', compact('subTitle', 'periods', 'bill')); + return view('bills.edit', compact('subTitle', 'periods', 'bill', 'defaultCurrency', 'currencies')); } /** @@ -296,6 +298,12 @@ class BillController extends Controller $request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore } + // do return to original bill form? + $return = 'false'; + if (1 === (int)$request->get('create_another')) { + $return = 'true'; + } + // find first rule group, or create one: $count = $ruleGroupRepository->count(); if ($count === 0) { @@ -309,9 +317,10 @@ class BillController extends Controller $group = $ruleGroupRepository->getActiveGroups(auth()->user())->first(); } - // redirect to page that will create a new rule. - return redirect(route('rules.create', [$group->id]) . '?fromBill=' . $bill->id); + $params = http_build_query(['fromBill' => $bill->id, 'return' => $return]); + + return redirect(route('rules.create', [$group->id]) . '?' . $params); } /** diff --git a/app/Http/Controllers/Json/AutoCompleteController.php b/app/Http/Controllers/Json/AutoCompleteController.php index ef8dbb2b76..b01069520e 100644 --- a/app/Http/Controllers/Json/AutoCompleteController.php +++ b/app/Http/Controllers/Json/AutoCompleteController.php @@ -31,6 +31,7 @@ use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Bill\BillRepositoryInterface; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; use FireflyIII\Repositories\Category\CategoryRepositoryInterface; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use FireflyIII\Repositories\Tag\TagRepositoryInterface; use FireflyIII\Support\CacheProperties; @@ -119,6 +120,19 @@ class AutoCompleteController extends Controller return response()->json($return); } + /** + * @param CurrencyRepositoryInterface $repository + * + * @return \Illuminate\Http\JsonResponse + */ + public function currencyNames(CurrencyRepositoryInterface $repository) + { + $return = $repository->get()->pluck('name')->toArray(); + sort($return); + + return response()->json($return); + } + /** * Returns a JSON list of all beneficiaries. * diff --git a/app/Http/Controllers/RuleController.php b/app/Http/Controllers/RuleController.php index 21c0ae694c..f4be740679 100644 --- a/app/Http/Controllers/RuleController.php +++ b/app/Http/Controllers/RuleController.php @@ -29,6 +29,7 @@ use FireflyIII\Http\Requests\SelectTransactionsRequest; use FireflyIII\Http\Requests\TestRuleFormRequest; use FireflyIII\Jobs\ExecuteRuleOnExistingTransactions; use FireflyIII\Models\AccountType; +use FireflyIII\Models\Bill; use FireflyIII\Models\Rule; use FireflyIII\Models\RuleAction; use FireflyIII\Models\RuleGroup; @@ -39,8 +40,10 @@ use FireflyIII\Repositories\Rule\RuleRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use FireflyIII\TransactionRules\TransactionMatcher; use Illuminate\Http\Request; +use Log; use Preferences; use Session; +use Throwable; use View; /** @@ -73,81 +76,47 @@ class RuleController extends Controller * * @return View * - - */ public function create(Request $request, RuleGroupRepositoryInterface $ruleGroupRepository, BillRepositoryInterface $billRepository, RuleGroup $ruleGroup) { - $bill = null; - $billId = (int)$request->get('fromBill'); - $preFilled = []; - // count for possible present previous entered triggers/actions. - $triggerCount = 0; - $actionCount = 0; + $bill = null; + $billId = (int)$request->get('fromBill'); + $preFilled = []; + $groups = ExpandedForm::makeSelectList($ruleGroupRepository->get()); + $oldTriggers = []; + $oldActions = []; + $returnToBill = false; - // collection of those triggers/actions. - $oldTriggers = []; - $oldActions = []; + if ($request->get('return') === 'true') { + $returnToBill = true; + } + + // has bill? + if ($billId > 0) { + $bill = $billRepository->find($billId); + } // has old input? if ($request->old()) { - // process old triggers. - $oldTriggers = $this->getPreviousTriggers($request); - $triggerCount = \count($oldTriggers); - - // process old actions + $oldTriggers = $this->getPreviousTriggers($request); $oldActions = $this->getPreviousActions($request); - $actionCount = \count($oldActions); + } - if ($billId > 0) { - $bill = $billRepository->find($billId); + // has existing bill refered to in URI? + if (null !== $bill && !$request->old()) { + // create some sensible defaults: $preFilled['title'] = trans('firefly.new_rule_for_bill_title', ['name' => $bill->name]); $preFilled['description'] = trans('firefly.new_rule_for_bill_description', ['name' => $bill->name]); $request->session()->flash('preFilled', $preFilled); - // pretend there are old triggers, so the page will fill them in: - $oldTriggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => 'amount_more', - 'oldValue' => round($bill->amount_min,12), - 'oldChecked' => false, - 'count' => 1, - ] - )->render(); - $oldTriggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => 'amount_less', - 'oldValue' => round($bill->amount_max,12), - 'oldChecked' => false, - 'count' => 2, - ] - )->render(); - $oldTriggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => 'description_contains', - 'oldValue' => $bill->name,12, - 'oldChecked' => false, - 'count' => 3, - ] - )->render(); - - $oldActions[] = view( - 'rules.partials.action', - [ - 'oldAction' => 'link_to_bill', - 'oldValue' => $bill->name, - 'oldChecked' => false, - 'count' => 1, - ] - )->render(); - + // get triggers and actions for bill: + $oldTriggers = $this->getTriggersForBill($bill); + $oldActions = $this->getActionsForBill($bill); } - + $triggerCount = \count($oldTriggers); + $actionCount = \count($oldActions); $subTitleIcon = 'fa-clone'; $subTitle = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]); @@ -159,7 +128,10 @@ class RuleController extends Controller return view( 'rules.rule.create', - compact('subTitleIcon', 'oldTriggers', 'preFilled', 'bill', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle') + compact( + 'subTitleIcon', 'oldTriggers', 'returnToBill', 'groups', 'preFilled', 'bill', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup', + 'subTitle' + ) ); } @@ -380,24 +352,32 @@ class RuleController extends Controller /** * @param RuleFormRequest $request * @param RuleRepositoryInterface $repository - * @param RuleGroup $ruleGroup * * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector */ - public function store(RuleFormRequest $request, RuleRepositoryInterface $repository, RuleGroup $ruleGroup) + public function store(RuleFormRequest $request, RuleRepositoryInterface $repository) { - $data = $request->getRuleData(); - $data['rule_group_id'] = $ruleGroup->id; - + $data = $request->getRuleData(); $rule = $repository->store($data); - Session::flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title])); + session()->flash('success', trans('firefly.stored_new_rule', ['title' => $rule->title])); Preferences::mark(); + // redirect to show bill. + if ($request->get('return_to_bill') === 'true' && (int)$request->get('bill_id') > 0) { + return redirect(route('bills.show', [(int)$request->get('bill_id')])); + } + + // redirect to new bill creation. + if ((int)$request->get('bill_id') > 0) { + return redirect(route('bills.create')); + } + + if (1 === (int)$request->get('create_another')) { // @codeCoverageIgnoreStart Session::put('rules.create.fromStore', true); - return redirect(route('rules.create', [$ruleGroup]))->withInput(); + return redirect(route('rules.create', [$data['rule_group_id']]))->withInput(); // @codeCoverageIgnoreEnd } @@ -416,7 +396,6 @@ class RuleController extends Controller * * @return \Illuminate\Http\JsonResponse * - */ public function testTriggers(TestRuleFormRequest $request) { @@ -586,6 +565,31 @@ class RuleController extends Controller } } + /** + * @param Bill $bill + * + * @return array + */ + private function getActionsForBill(Bill $bill): array + { + $actions = []; + try { + $actions[] = view( + 'rules.partials.action', + [ + 'oldAction' => 'link_to_bill', + 'oldValue' => $bill->name, + 'oldChecked' => false, + 'count' => 1, + ] + )->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Throwable was thrown in getActionsForBill(): %s', $e->getMessage())); + } + + return $actions; + } + /** * @param Rule $rule * @@ -600,16 +604,20 @@ class RuleController extends Controller /** @var RuleAction $entry */ foreach ($rule->ruleActions as $entry) { - $count = ($index + 1); - $actions[] = view( - 'rules.partials.action', - [ - 'oldAction' => $entry->action_type, - 'oldValue' => $entry->action_value, - 'oldChecked' => $entry->stop_processing, - 'count' => $count, - ] - )->render(); + $count = ($index + 1); + try { + $actions[] = view( + 'rules.partials.action', + [ + 'oldAction' => $entry->action_type, + 'oldValue' => $entry->action_value, + 'oldChecked' => $entry->stop_processing, + 'count' => $count, + ] + )->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Throwable was thrown in getCurrentActions(): %s', $e->getMessage())); + } ++$index; } @@ -631,16 +639,20 @@ class RuleController extends Controller /** @var RuleTrigger $entry */ foreach ($rule->ruleTriggers as $entry) { if ('user_action' !== $entry->trigger_type) { - $count = ($index + 1); - $triggers[] = view( - 'rules.partials.trigger', - [ - 'oldTrigger' => $entry->trigger_type, - 'oldValue' => $entry->trigger_value, - 'oldChecked' => $entry->stop_processing, - 'count' => $count, - ] - )->render(); + $count = ($index + 1); + try { + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => $entry->trigger_type, + 'oldValue' => $entry->trigger_value, + 'oldChecked' => $entry->stop_processing, + 'count' => $count, + ] + )->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Throwable was thrown in getCurrentTriggers(): %s', $e->getMessage())); + } ++$index; } } @@ -662,17 +674,21 @@ class RuleController extends Controller /** @var array $oldActions */ $oldActions = is_array($request->old('rule-action')) ? $request->old('rule-action') : []; foreach ($oldActions as $index => $entry) { - $count = ($newIndex + 1); - $checked = isset($request->old('rule-action-stop')[$index]) ? true : false; - $actions[] = view( - 'rules.partials.action', - [ - 'oldAction' => $entry, - 'oldValue' => $request->old('rule-action-value')[$index], - 'oldChecked' => $checked, - 'count' => $count, - ] - )->render(); + $count = ($newIndex + 1); + $checked = isset($request->old('rule-action-stop')[$index]) ? true : false; + try { + $actions[] = view( + 'rules.partials.action', + [ + 'oldAction' => $entry, + 'oldValue' => $request->old('rule-action-value')[$index], + 'oldChecked' => $checked, + 'count' => $count, + ] + )->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Throwable was thrown in getPreviousActions(): %s', $e->getMessage())); + } ++$newIndex; } @@ -695,16 +711,77 @@ class RuleController extends Controller foreach ($oldTriggers as $index => $entry) { $count = ($newIndex + 1); $oldChecked = isset($request->old('rule-trigger-stop')[$index]) ? true : false; + try { + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => $entry, + 'oldValue' => $request->old('rule-trigger-value')[$index], + 'oldChecked' => $oldChecked, + 'count' => $count, + ] + )->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Throwable was thrown in getPreviousTriggers(): %s', $e->getMessage())); + } + ++$newIndex; + } + + return $triggers; + } + + /** + * Create fake triggers to match the bill's properties + * + * @param Bill $bill + * + * @return array + */ + private function getTriggersForBill(Bill $bill): array + { + $triggers = []; + try { $triggers[] = view( 'rules.partials.trigger', [ - 'oldTrigger' => $entry, - 'oldValue' => $request->old('rule-trigger-value')[$index], - 'oldChecked' => $oldChecked, - 'count' => $count, + 'oldTrigger' => 'currency_is', + 'oldValue' => $bill->transactionCurrency()->first()->name, + 'oldChecked' => false, + 'count' => 1, ] )->render(); - ++$newIndex; + + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => 'amount_more', + 'oldValue' => round($bill->amount_min, 12), + 'oldChecked' => false, + 'count' => 2, + ] + )->render(); + + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => 'amount_less', + 'oldValue' => round($bill->amount_max, 12), + 'oldChecked' => false, + 'count' => 3, + ] + )->render(); + $triggers[] = view( + 'rules.partials.trigger', + [ + 'oldTrigger' => 'description_contains', + 'oldValue' => $bill->name, 12, + 'oldChecked' => false, + 'count' => 4, + ] + )->render(); + } catch (Throwable $e) { + Log::debug(sprintf('Throwable was thrown in getTriggersForBill(): %s', $e->getMessage())); + Log::debug($e->getTraceAsString()); } return $triggers; @@ -723,7 +800,7 @@ class RuleController extends Controller 'rule-trigger-values' => $request->get('rule-trigger-value'), 'rule-trigger-stop' => $request->get('rule-trigger-stop'), ]; - if (is_array($data['rule-triggers'])) { + if (\is_array($data['rule-triggers'])) { foreach ($data['rule-triggers'] as $index => $triggerType) { $data['rule-trigger-stop'][$index] = (int)($data['rule-trigger-stop'][$index] ?? 0.0); $triggers[] = [ diff --git a/app/Http/Requests/BillFormRequest.php b/app/Http/Requests/BillFormRequest.php index e8e3677e41..7b0b54083f 100644 --- a/app/Http/Requests/BillFormRequest.php +++ b/app/Http/Requests/BillFormRequest.php @@ -42,15 +42,15 @@ class BillFormRequest extends Request public function getBillData() { return [ - 'name' => $this->string('name'), - 'amount_min' => $this->string('amount_min'), - 'currency_id' => $this->integer('currency_id'), - 'amount_max' => $this->string('amount_max'), - 'date' => $this->date('date'), - 'repeat_freq' => $this->string('repeat_freq'), - 'skip' => $this->integer('skip'), - 'active' => $this->boolean('active'), - 'notes' => $this->string('notes'), + 'name' => $this->string('name'), + 'amount_min' => $this->string('amount_min'), + 'transaction_currency_id' => $this->integer('transaction_currency_id'), + 'amount_max' => $this->string('amount_max'), + 'date' => $this->date('date'), + 'repeat_freq' => $this->string('repeat_freq'), + 'skip' => $this->integer('skip'), + 'active' => $this->boolean('active'), + 'notes' => $this->string('notes'), ]; } @@ -66,15 +66,15 @@ class BillFormRequest extends Request } // is OK $rules = [ - 'name' => $nameRule, - 'amount_min' => 'required|numeric|more:0', - 'amount_max' => 'required|numeric|more:0', - 'currency_id' => 'required|exists:transaction_currencies,id', - 'date' => 'required|date', - 'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly', - 'skip' => 'required|between:0,31', - 'automatch' => 'in:1', - 'active' => 'in:1', + 'name' => $nameRule, + 'amount_min' => 'required|numeric|more:0', + 'amount_max' => 'required|numeric|more:0', + 'transaction_currency_id' => 'required|exists:transaction_currencies,id', + 'date' => 'required|date', + 'repeat_freq' => 'required|in:weekly,monthly,quarterly,half-year,yearly', + 'skip' => 'required|between:0,31', + 'automatch' => 'in:1', + 'active' => 'in:1', ]; return $rules; diff --git a/app/Models/Bill.php b/app/Models/Bill.php index d8513b60d6..73e5d93b49 100644 --- a/app/Models/Bill.php +++ b/app/Models/Bill.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace FireflyIII\Models; use Crypt; +use FireflyIII\User; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; @@ -57,7 +58,7 @@ class Bill extends Model */ protected $fillable = ['name', 'match', 'amount_min', 'match_encrypted', 'name_encrypted', 'user_id', 'amount_max', 'date', 'repeat_freq', 'skip', - 'automatch', 'active','currency_id']; + 'automatch', 'active', 'transaction_currency_id']; /** * @var array */ @@ -179,13 +180,22 @@ class Bill extends Model $this->attributes['name_encrypted'] = $encrypt; } + /** + * @codeCoverageIgnore + * @return BelongsTo + */ + public function transactionCurrency(): BelongsTo + { + return $this->belongsTo(TransactionCurrency::class); + } + /** * @codeCoverageIgnore * @return HasMany */ public function transactionJournals(): HasMany { - return $this->hasMany('FireflyIII\Models\TransactionJournal'); + return $this->hasMany(TransactionJournal::class); } /** @@ -194,6 +204,6 @@ class Bill extends Model */ public function user(): BelongsTo { - return $this->belongsTo('FireflyIII\User'); + return $this->belongsTo(User::class); } } diff --git a/app/Services/Internal/Update/BillUpdateService.php b/app/Services/Internal/Update/BillUpdateService.php index 69cf09f38d..34edc0c92f 100644 --- a/app/Services/Internal/Update/BillUpdateService.php +++ b/app/Services/Internal/Update/BillUpdateService.php @@ -42,20 +42,15 @@ class BillUpdateService */ public function update(Bill $bill, array $data): Bill { - - $matchArray = explode(',', $data['match']); - $matchArray = array_unique($matchArray); - $match = implode(',', $matchArray); - - $bill->name = $data['name']; - $bill->match = $match; - $bill->amount_min = $data['amount_min']; - $bill->amount_max = $data['amount_max']; - $bill->date = $data['date']; - $bill->repeat_freq = $data['repeat_freq']; - $bill->skip = $data['skip']; - $bill->automatch = $data['automatch']; - $bill->active = $data['active']; + $bill->name = $data['name']; + $bill->amount_min = $data['amount_min']; + $bill->amount_max = $data['amount_max']; + $bill->date = $data['date']; + $bill->transaction_currency_id = $data['transaction_currency_id']; + $bill->repeat_freq = $data['repeat_freq']; + $bill->skip = $data['skip']; + $bill->automatch = true; + $bill->active = $data['active']; $bill->save(); // update note: diff --git a/app/TransactionRules/Triggers/CurrencyIs.php b/app/TransactionRules/Triggers/CurrencyIs.php new file mode 100644 index 0000000000..120a938ea0 --- /dev/null +++ b/app/TransactionRules/Triggers/CurrencyIs.php @@ -0,0 +1,92 @@ +. + */ +declare(strict_types=1); + +namespace FireflyIII\TransactionRules\Triggers; + +use FireflyIII\Models\Transaction; +use FireflyIII\Models\TransactionJournal; +use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use Log; + +/** + * Class CurrencyIs. + */ +final class CurrencyIs extends AbstractTrigger implements TriggerInterface +{ + /** + * A trigger is said to "match anything", or match any given transaction, + * when the trigger value is very vague or has no restrictions. Easy examples + * are the "AmountMore"-trigger combined with an amount of 0: any given transaction + * has an amount of more than zero! Other examples are all the "Description"-triggers + * which have hard time handling empty trigger values such as "" or "*" (wild cards). + * + * If the user tries to create such a trigger, this method MUST return true so Firefly III + * can stop the storing / updating the trigger. If the trigger is in any way restrictive + * (even if it will still include 99.9% of the users transactions), this method MUST return + * false. + * + * @param null $value + * + * @return bool + */ + public static function willMatchEverything($value = null) + { + if (null !== $value) { + return false; + } + + Log::error(sprintf('Cannot use %s with a null value.', self::class)); + + return true; + } + + /** + * Returns true when description is X + * + * @param TransactionJournal $journal + * + * @return bool + */ + public function triggered(TransactionJournal $journal): bool + { + /** @var CurrencyRepositoryInterface $repository */ + $repository = app(CurrencyRepositoryInterface::class); + $currency = $repository->findByNameNull($this->triggerValue); + $hit = true; + if (null !== $currency) { + /** @var Transaction $transaction */ + foreach ($journal->transactions as $transaction) { + if ((int)$transaction->transaction_currency_id !== (int)$currency->id) { + Log::debug( + sprintf( + 'Trigger CurrencyIs: Transaction #%d in journal #%d uses currency %d instead of sought for #%d. No hit!', + $transaction->id, $journal->id, $transaction->transaction_currency_id, $currency->id + ) + ); + $hit = false; + } + } + } + + return $hit; + } +} diff --git a/config/firefly.php b/config/firefly.php index c23585c017..7d0f8c530d 100644 --- a/config/firefly.php +++ b/config/firefly.php @@ -25,6 +25,7 @@ use FireflyIII\TransactionRules\Triggers\AmountLess; use FireflyIII\TransactionRules\Triggers\AmountMore; use FireflyIII\TransactionRules\Triggers\BudgetIs; use FireflyIII\TransactionRules\Triggers\CategoryIs; +use FireflyIII\TransactionRules\Triggers\CurrencyIs; use FireflyIII\TransactionRules\Triggers\DescriptionContains; use FireflyIII\TransactionRules\Triggers\DescriptionEnds; use FireflyIII\TransactionRules\Triggers\DescriptionIs; @@ -89,7 +90,7 @@ return [ 'encryption' => null === env('USE_ENCRYPTION') || env('USE_ENCRYPTION') === true, 'version' => '4.7.2.2', 'api_version' => '0.1', - 'db_version' => 2, + 'db_version' => 3, 'maxUploadSize' => 15242880, 'allowedMimes' => [ /* plain files */ @@ -324,6 +325,7 @@ return [ 'category_is' => CategoryIs::class, 'budget_is' => BudgetIs::class, 'tag_is' => TagIs::class, + 'currency_is' => CurrencyIs::class, 'has_attachments' => HasAttachment::class, 'has_no_category' => HasNoCategory::class, 'has_any_category' => HasAnyCategory::class, @@ -362,6 +364,7 @@ return [ 'set_budget', 'add_tag', 'remove_tag', + 'link_to_bill', 'set_description', 'append_description', 'prepend_description', diff --git a/config/upgrade.php b/config/upgrade.php index 7164d9991d..afdc4a4f88 100644 --- a/config/upgrade.php +++ b/config/upgrade.php @@ -28,6 +28,7 @@ return [ '4.3' => 'Make sure you run the migrations and clear your cache. If you need more help, please check Github or the Firefly III website.', '4.6.3' => 'This will be the last version to require PHP7.0. Future versions will require PHP7.1 minimum.', '4.6.4' => 'This version of Firefly III requires PHP7.1.', + '4.7.3' => 'This version of Firefly III handles bills differently. See https://goo.gl/zkVdrF for more information.', ], 'install' => [ diff --git a/database/migrations/2018_04_07_210913_changes_for_v473.php b/database/migrations/2018_04_07_210913_changes_for_v473.php index 02ff124d53..abeee6eae6 100644 --- a/database/migrations/2018_04_07_210913_changes_for_v473.php +++ b/database/migrations/2018_04_07_210913_changes_for_v473.php @@ -28,8 +28,8 @@ class ChangesForV473 extends Migration Schema::table( 'bills', function (Blueprint $table) { - $table->integer('currency_id', false, true)->nullable()->after('user_id'); - $table->foreign('currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); + $table->integer('transaction_currency_id', false, true)->nullable()->after('user_id'); + $table->foreign('transaction_currency_id')->references('id')->on('transaction_currencies')->onDelete('set null'); } ); } diff --git a/public/js/ff/rules/create-edit.js b/public/js/ff/rules/create-edit.js index a9b5ed0e86..ef859e255c 100644 --- a/public/js/ff/rules/create-edit.js +++ b/public/js/ff/rules/create-edit.js @@ -285,6 +285,9 @@ function updateTriggerInput(selectList) { input.prop('disabled', true); input.typeahead('destroy'); break; + case 'currency_is': + createAutoComplete(input, 'json/currency-names'); + break; default: input.typeahead('destroy'); break; diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index eff5ccca08..162a18e399 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -345,6 +345,8 @@ return [ 'rule_trigger_budget_is' => 'Budget is ":trigger_value"', 'rule_trigger_tag_is_choice' => '(A) tag is..', 'rule_trigger_tag_is' => 'A tag is ":trigger_value"', + 'rule_trigger_currency_is_choice' => 'Transaction currency is..', + 'rule_trigger_currency_is' => 'Transaction currency is ":trigger_value"', 'rule_trigger_has_attachments_choice' => 'Has at least this many attachments', 'rule_trigger_has_attachments' => 'Has at least :trigger_value attachment(s)', 'rule_trigger_store_journal' => 'When a transaction is created', diff --git a/resources/lang/en_US/form.php b/resources/lang/en_US/form.php index 4c132ced51..23b88d81d6 100644 --- a/resources/lang/en_US/form.php +++ b/resources/lang/en_US/form.php @@ -1,5 +1,6 @@ 'Repeats', 'journal_currency_id' => 'Currency', 'currency_id' => 'Currency', + 'transaction_currency_id' => 'Currency', 'attachments' => 'Attachments', 'journal_amount' => 'Amount', 'journal_source_account_name' => 'Revenue account (source)', diff --git a/resources/views/bills/create.twig b/resources/views/bills/create.twig index 4c32042a39..a2663bff18 100644 --- a/resources/views/bills/create.twig +++ b/resources/views/bills/create.twig @@ -17,7 +17,7 @@
{{ ExpandedForm.text('name') }} - {{ ExpandedForm.select('currency_id',currencies, defaultCurrency.id) }} + {{ ExpandedForm.select('transaction_currency_id',currencies, defaultCurrency.id) }} {{ ExpandedForm.amountNoCurrency('amount_min') }} {{ ExpandedForm.amountNoCurrency('amount_max') }} {{ ExpandedForm.date('date',phpdate('Y-m-d')) }} diff --git a/resources/views/bills/edit.twig b/resources/views/bills/edit.twig index 583a7a2733..e64d063905 100644 --- a/resources/views/bills/edit.twig +++ b/resources/views/bills/edit.twig @@ -18,9 +18,9 @@
{{ ExpandedForm.text('name') }} - {{ ExpandedForm.tags('match') }} - {{ ExpandedForm.amount('amount_min') }} - {{ ExpandedForm.amount('amount_max') }} + {{ ExpandedForm.select('transaction_currency_id',currencies) }} + {{ ExpandedForm.amountNoCurrency('amount_min') }} + {{ ExpandedForm.amountNoCurrency('amount_max') }} {{ ExpandedForm.date('date',bill.date.format('Y-m-d')) }} {{ ExpandedForm.select('repeat_freq',periods) }}
@@ -37,7 +37,6 @@ {{ ExpandedForm.textarea('notes',null,{helpText: trans('firefly.field_supports_markdown')}) }} {{ ExpandedForm.file('attachments[]', {'multiple': 'multiple','helpText': trans('firefly.upload_max_file_size', {'size': uploadSize|filesize}) }) }} {{ ExpandedForm.integer('skip') }} - {{ ExpandedForm.checkbox('automatch',1) }} {{ ExpandedForm.checkbox('active',1) }} diff --git a/resources/views/rules/rule/create.twig b/resources/views/rules/rule/create.twig index b27c2411a2..1fddf8f2af 100644 --- a/resources/views/rules/rule/create.twig +++ b/resources/views/rules/rule/create.twig @@ -6,9 +6,12 @@ {% block content %} -
+ + + + {% if bill %}
@@ -35,6 +38,7 @@
{{ ExpandedForm.text('title') }} {{ ExpandedForm.select('trigger',allJournalTriggers()) }} + {{ ExpandedForm.select('rule_group_id',groups, ruleGroup.id) }} {{ ExpandedForm.checkbox('stop_processing',1,null, {helpText: trans('firefly.rule_help_stop_processing')}) }}
diff --git a/routes/web.php b/routes/web.php index 9c156f7e6a..a7c91ce196 100755 --- a/routes/web.php +++ b/routes/web.php @@ -501,6 +501,7 @@ Route::group( Route::get('budgets', ['uses' => 'Json\AutoCompleteController@budgets', 'as' => 'budgets']); Route::get('tags', ['uses' => 'Json\AutoCompleteController@tags', 'as' => 'tags']); Route::get('bills', ['uses' => 'Json\AutoCompleteController@bills', 'as' => 'bills']); + Route::get('currency-names', ['uses' => 'Json\AutoCompleteController@currencyNames', 'as' => 'currency-names']); Route::get('transaction-journals/all', ['uses' => 'Json\AutoCompleteController@allTransactionJournals', 'as' => 'all-transaction-journals']); Route::get('transaction-journals/with-id/{tj}', ['uses' => 'Json\AutoCompleteController@journalsWithId', 'as' => 'journals-with-id']); Route::get('transaction-journals/{what}', ['uses' => 'Json\AutoCompleteController@transactionJournals', 'as' => 'transaction-journals']); @@ -723,7 +724,7 @@ Route::group( Route::post('trigger/order/{rule}', ['uses' => 'RuleController@reorderRuleTriggers', 'as' => 'reorder-triggers']); Route::post('action/order/{rule}', ['uses' => 'RuleController@reorderRuleActions', 'as' => 'reorder-actions']); - Route::post('store/{ruleGroup}', ['uses' => 'RuleController@store', 'as' => 'store']); + Route::post('store', ['uses' => 'RuleController@store', 'as' => 'store']); Route::post('update/{rule}', ['uses' => 'RuleController@update', 'as' => 'update']); Route::post('destroy/{rule}', ['uses' => 'RuleController@destroy', 'as' => 'destroy']); Route::post('execute/{rule}', ['uses' => 'RuleController@execute', 'as' => 'execute']);