mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2026-05-03 20:56:21 +00:00
Code for #1324
This commit is contained in:
@@ -28,13 +28,20 @@ use FireflyIII\Models\Account;
|
||||
use FireflyIII\Models\AccountMeta;
|
||||
use FireflyIII\Models\AccountType;
|
||||
use FireflyIII\Models\Attachment;
|
||||
use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Models\Rule;
|
||||
use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\RuleTrigger;
|
||||
use FireflyIII\Models\Transaction;
|
||||
use FireflyIII\Models\TransactionCurrency;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Models\TransactionJournalMeta;
|
||||
use FireflyIII\Models\TransactionType;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Database\QueryException;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -70,6 +77,7 @@ class UpgradeDatabase extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$this->migrateBillsToRules();
|
||||
$this->setTransactionIdentifier();
|
||||
$this->updateAccountCurrencies();
|
||||
$this->createNewTypes();
|
||||
@@ -79,9 +87,118 @@ class UpgradeDatabase extends Command
|
||||
$this->line('Done updating currency information..');
|
||||
$this->migrateNotes();
|
||||
$this->migrateAttachmentData();
|
||||
$this->info('Firefly III database is up to date.');
|
||||
|
||||
return;
|
||||
$this->info('Firefly III database is up to date.');
|
||||
}
|
||||
|
||||
public function migrateBillsToRules()
|
||||
{
|
||||
foreach (User::get() as $user) {
|
||||
/** @var Preference $lang */
|
||||
$lang = Preferences::getForUser($user, 'language', 'en_US');
|
||||
$groupName = (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data);
|
||||
$ruleGroup = $user->ruleGroups()->where('title', $groupName)->first();
|
||||
$currencyPreference = Preferences::getForUser($user, 'currencyPreference', config('firefly.default_currency', 'EUR'));
|
||||
$currency = TransactionCurrency::where('code', $currencyPreference->data)->first();
|
||||
|
||||
if ($ruleGroup === null) {
|
||||
$array = RuleGroup::get(['order'])->pluck('order')->toArray();
|
||||
$order = count($array) > 0 ? max($array) + 1 : 1;
|
||||
$ruleGroup = RuleGroup::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'title' => (string)trans('firefly.rulegroup_for_bills_title', [], $lang->data),
|
||||
'description' => (string)trans('firefly.rulegroup_for_bills_description', [], $lang->data),
|
||||
'order' => $order,
|
||||
'active' => 1,
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// loop bills.
|
||||
$order = 1;
|
||||
/** @var Bill $bill */
|
||||
foreach ($user->bills()->get() as $bill) {
|
||||
if ($bill->match !== 'MIGRATED_TO_RULES') {
|
||||
$rule = Rule::create(
|
||||
[
|
||||
'user_id' => $user->id,
|
||||
'rule_group_id' => $ruleGroup->id,
|
||||
'title' => (string)trans('firefly.rule_for_bill_title', ['name' => $bill->name], $lang->data),
|
||||
'description' => (string)trans('firefly.rule_for_bill_description', ['name' => $bill->name], $lang->data),
|
||||
'order' => $order,
|
||||
'active' => 1,
|
||||
'stop_processing' => 1,
|
||||
]
|
||||
);
|
||||
// add default trigger
|
||||
RuleTrigger::create(
|
||||
[
|
||||
'rule_id' => $rule->id,
|
||||
'trigger_type' => 'user_action',
|
||||
'trigger_value' => 'store-journal',
|
||||
'active' => 1,
|
||||
'stop_processing' => 0,
|
||||
'order' => 1,
|
||||
]
|
||||
);
|
||||
// add trigger for description
|
||||
$match = implode(' ', explode(',', $bill->match));
|
||||
RuleTrigger::create(
|
||||
[
|
||||
'rule_id' => $rule->id,
|
||||
'trigger_type' => 'description_is',
|
||||
'trigger_value' => $match,
|
||||
'active' => 1,
|
||||
'stop_processing' => 0,
|
||||
'order' => 2,
|
||||
]
|
||||
);
|
||||
|
||||
// add triggers for amounts:
|
||||
RuleTrigger::create(
|
||||
[
|
||||
'rule_id' => $rule->id,
|
||||
'trigger_type' => 'amount_less',
|
||||
'trigger_value' => round($bill->amount_max, $currency->decimal_places),
|
||||
'active' => 1,
|
||||
'stop_processing' => 0,
|
||||
'order' => 3,
|
||||
]
|
||||
);
|
||||
RuleTrigger::create(
|
||||
[
|
||||
'rule_id' => $rule->id,
|
||||
'trigger_type' => 'amount_more',
|
||||
'trigger_value' => round($bill->amount_min, $currency->decimal_places),
|
||||
'active' => 1,
|
||||
'stop_processing' => 0,
|
||||
'order' => 4,
|
||||
]
|
||||
);
|
||||
|
||||
// create action
|
||||
RuleAction::create(
|
||||
[
|
||||
'rule_id' => $rule->id,
|
||||
'action_type' => 'link_to_bill',
|
||||
'action_value' => $bill->name,
|
||||
'order' => 1,
|
||||
'active' => 1,
|
||||
'stop_processing' => 0,
|
||||
]
|
||||
);
|
||||
|
||||
$order++;
|
||||
//$bill->match = 'MIGRATED_TO_RULES';
|
||||
$bill->save();
|
||||
$this->line(sprintf('Updated bill #%d ("%s") so it will use rules.', $bill->id, $bill->name));
|
||||
}
|
||||
}
|
||||
|
||||
$this->line('Exit');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,22 +45,19 @@ class BillFactory
|
||||
*/
|
||||
public function create(array $data): ?Bill
|
||||
{
|
||||
$matchArray = explode(',', $data['match']);
|
||||
$matchArray = array_unique($matchArray);
|
||||
$match = implode(',', $matchArray);
|
||||
|
||||
/** @var Bill $bill */
|
||||
$bill = Bill::create(
|
||||
[
|
||||
'name' => $data['name'],
|
||||
'match' => $match,
|
||||
'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' => $data['automatch'],
|
||||
'automatch' => true,
|
||||
'active' => $data['active'],
|
||||
]
|
||||
);
|
||||
@@ -94,7 +91,7 @@ class BillFactory
|
||||
}
|
||||
|
||||
// then find by name:
|
||||
if (strlen($billName) > 0) {
|
||||
if (\strlen($billName) > 0) {
|
||||
$bill = $this->findByName($billName);
|
||||
if (null !== $bill) {
|
||||
return $bill;
|
||||
|
||||
@@ -71,7 +71,7 @@ class TagFactory
|
||||
|
||||
/** @var Tag $object */
|
||||
foreach ($this->tags as $object) {
|
||||
if ($object->tag === $tag) {
|
||||
if (strtolower($object->tag) === strtolower($tag)) {
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use ExpandedForm;
|
||||
use FireflyIII\Helpers\Attachments\AttachmentHelperInterface;
|
||||
use FireflyIII\Helpers\Collector\JournalCollectorInterface;
|
||||
use FireflyIII\Http\Requests\BillFormRequest;
|
||||
@@ -29,6 +30,8 @@ use FireflyIII\Models\Bill;
|
||||
use FireflyIII\Models\Note;
|
||||
use FireflyIII\Models\TransactionJournal;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\Transformers\BillTransformer;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Collection;
|
||||
@@ -76,13 +79,15 @@ class BillController extends Controller
|
||||
*
|
||||
* @return View
|
||||
*/
|
||||
public function create(Request $request)
|
||||
public function create(Request $request, CurrencyRepositoryInterface $repository)
|
||||
{
|
||||
$periods = [];
|
||||
foreach (config('firefly.bill_periods') as $current) {
|
||||
$periods[$current] = strtolower((string)trans('firefly.repeat_freq_' . $current));
|
||||
}
|
||||
$subTitle = trans('firefly.create_new_bill');
|
||||
$subTitle = trans('firefly.create_new_bill');
|
||||
$defaultCurrency = app('amount')->getDefaultCurrency();
|
||||
$currencies = ExpandedForm::makeSelectList($repository->get());
|
||||
|
||||
// put previous url in session if not redirect from store (not "create another").
|
||||
if (true !== session('bills.create.fromStore')) {
|
||||
@@ -90,7 +95,7 @@ class BillController extends Controller
|
||||
}
|
||||
$request->session()->forget('bills.create.fromStore');
|
||||
|
||||
return view('bills.create', compact('periods', 'subTitle'));
|
||||
return view('bills.create', compact('periods', 'subTitle', 'currencies', 'defaultCurrency'));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,7 +275,7 @@ class BillController extends Controller
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function store(BillFormRequest $request, BillRepositoryInterface $repository)
|
||||
public function store(BillFormRequest $request, BillRepositoryInterface $repository, RuleGroupRepositoryInterface $ruleGroupRepository)
|
||||
{
|
||||
$billData = $request->getBillData();
|
||||
$bill = $repository->store($billData);
|
||||
@@ -291,16 +296,22 @@ class BillController extends Controller
|
||||
$request->session()->flash('info', $this->attachments->getMessages()->get('attachments')); // @codeCoverageIgnore
|
||||
}
|
||||
|
||||
if (1 === (int)$request->get('create_another')) {
|
||||
// @codeCoverageIgnoreStart
|
||||
$request->session()->put('bills.create.fromStore', true);
|
||||
|
||||
return redirect(route('bills.create'))->withInput();
|
||||
// @codeCoverageIgnoreEnd
|
||||
// find first rule group, or create one:
|
||||
$count = $ruleGroupRepository->count();
|
||||
if ($count === 0) {
|
||||
$data = [
|
||||
'title' => (string)trans('firefly.rulegroup_for_bills_title'),
|
||||
'description' => (string)trans('firefly.rulegroup_for_bills_description'),
|
||||
];
|
||||
$group = $ruleGroupRepository->store($data);
|
||||
}
|
||||
if ($count > 0) {
|
||||
$group = $ruleGroupRepository->getActiveGroups(auth()->user())->first();
|
||||
}
|
||||
|
||||
// redirect to previous URL.
|
||||
return redirect($this->getPreviousUri('bills.create.uri'));
|
||||
|
||||
// redirect to page that will create a new rule.
|
||||
return redirect(route('rules.create', [$group->id]) . '?fromBill=' . $bill->id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -34,6 +34,7 @@ use FireflyIII\Models\RuleAction;
|
||||
use FireflyIII\Models\RuleGroup;
|
||||
use FireflyIII\Models\RuleTrigger;
|
||||
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
|
||||
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
|
||||
use FireflyIII\Repositories\Rule\RuleRepositoryInterface;
|
||||
use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface;
|
||||
use FireflyIII\TransactionRules\TransactionMatcher;
|
||||
@@ -75,8 +76,11 @@ class RuleController extends Controller
|
||||
|
||||
|
||||
*/
|
||||
public function create(Request $request, RuleGroup $ruleGroup)
|
||||
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;
|
||||
@@ -89,12 +93,60 @@ class RuleController extends Controller
|
||||
if ($request->old()) {
|
||||
// process old triggers.
|
||||
$oldTriggers = $this->getPreviousTriggers($request);
|
||||
$triggerCount = count($oldTriggers);
|
||||
$triggerCount = \count($oldTriggers);
|
||||
|
||||
// process old actions
|
||||
$oldActions = $this->getPreviousActions($request);
|
||||
$actionCount = count($oldActions);
|
||||
$actionCount = \count($oldActions);
|
||||
}
|
||||
if ($billId > 0) {
|
||||
$bill = $billRepository->find($billId);
|
||||
// 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();
|
||||
|
||||
}
|
||||
|
||||
|
||||
$subTitleIcon = 'fa-clone';
|
||||
$subTitle = trans('firefly.make_new_rule', ['title' => $ruleGroup->title]);
|
||||
@@ -107,7 +159,7 @@ class RuleController extends Controller
|
||||
|
||||
return view(
|
||||
'rules.rule.create',
|
||||
compact('subTitleIcon', 'oldTriggers', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle')
|
||||
compact('subTitleIcon', 'oldTriggers', 'preFilled', 'bill', 'oldActions', 'triggerCount', 'actionCount', 'ruleGroup', 'subTitle')
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,18 +42,15 @@ class BillFormRequest extends Request
|
||||
public function getBillData()
|
||||
{
|
||||
return [
|
||||
'name' => $this->string('name'),
|
||||
'match' => $this->string('match'),
|
||||
'amount_min' => $this->string('amount_min'),
|
||||
'amount_currency_id_amount_min' => $this->integer('amount_currency_id_amount_min'),
|
||||
'amount_currency_id_amount_max' => $this->integer('amount_currency_id_amount_max'),
|
||||
'amount_max' => $this->string('amount_max'),
|
||||
'date' => $this->date('date'),
|
||||
'repeat_freq' => $this->string('repeat_freq'),
|
||||
'skip' => $this->integer('skip'),
|
||||
'automatch' => $this->boolean('automatch'),
|
||||
'active' => $this->boolean('active'),
|
||||
'notes' => $this->string('notes'),
|
||||
'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'),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -62,25 +59,22 @@ class BillFormRequest extends Request
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name';
|
||||
$matchRule = 'required|between:1,255|uniqueObjectForUser:bills,match';
|
||||
$nameRule = 'required|between:1,255|uniqueObjectForUser:bills,name';
|
||||
if ($this->integer('id') > 0) {
|
||||
$nameRule .= ',' . $this->integer('id');
|
||||
$matchRule .= ',' . $this->integer('id');
|
||||
// todo is a fix to do this better.
|
||||
$nameRule .= ',' . $this->integer('id');
|
||||
}
|
||||
// is OK
|
||||
$rules = [
|
||||
'name' => $nameRule,
|
||||
'match' => $matchRule,
|
||||
'amount_min' => 'required|numeric|more:0',
|
||||
'amount_max' => 'required|numeric|more:0',
|
||||
'amount_currency_id_amount_min' => 'required|exists:transaction_currencies,id',
|
||||
'amount_currency_id_amount_max' => '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',
|
||||
'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;
|
||||
|
||||
@@ -57,7 +57,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',];
|
||||
'automatch', 'active','currency_id'];
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
||||
@@ -47,6 +47,8 @@ class Rule extends Model
|
||||
'order' => 'int',
|
||||
'stop_processing' => 'boolean',
|
||||
];
|
||||
/** @var array */
|
||||
protected $fillable = ['rule_group_id', 'order', 'active', 'title', 'description', 'user_id'];
|
||||
|
||||
/**
|
||||
* @param string $value
|
||||
|
||||
@@ -43,6 +43,9 @@ class RuleAction extends Model
|
||||
'stop_processing' => 'boolean',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
protected $fillable = ['rule_id', 'action_type', 'action_value', 'order', 'active', 'stop_processing'];
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
|
||||
@@ -43,6 +43,9 @@ class RuleTrigger extends Model
|
||||
'stop_processing' => 'boolean',
|
||||
];
|
||||
|
||||
/** @var array */
|
||||
protected $fillable = ['rule_id', 'trigger_type', 'trigger_value', 'order', 'active', 'stop_processing'];
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
* @return \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
|
||||
@@ -50,8 +50,6 @@ class RuleGroupRepository implements RuleGroupRepositoryInterface
|
||||
*
|
||||
* @return bool
|
||||
*
|
||||
|
||||
|
||||
*/
|
||||
public function destroy(RuleGroup $ruleGroup, ?RuleGroup $moveTo): bool
|
||||
{
|
||||
|
||||
@@ -51,7 +51,7 @@ trait JournalServiceTrait
|
||||
return; // @codeCoverageIgnore
|
||||
}
|
||||
foreach ($data['tags'] as $string) {
|
||||
if (strlen($string) > 0) {
|
||||
if (\strlen($string) > 0) {
|
||||
$tag = $factory->findOrCreate($string);
|
||||
$set[] = $tag->id;
|
||||
}
|
||||
|
||||
@@ -52,6 +52,34 @@ class ExpandedForm
|
||||
return $this->currencyField($name, 'amount', $value, $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param null $value
|
||||
* @param array $options
|
||||
*
|
||||
* @return string
|
||||
* @throws \FireflyIII\Exceptions\FireflyException
|
||||
* @throws \Throwable
|
||||
*/
|
||||
public function amountNoCurrency(string $name, $value = null, array $options = []): string
|
||||
{
|
||||
$label = $this->label($name, $options);
|
||||
$options = $this->expandOptionArray($name, $label, $options);
|
||||
$classes = $this->getHolderClasses($name);
|
||||
$value = $this->fillFieldValue($name, $value);
|
||||
$options['step'] = 'any';
|
||||
unset($options['currency'], $options['placeholder']);
|
||||
|
||||
// make sure value is formatted nicely:
|
||||
if (null !== $value && '' !== $value) {
|
||||
$value = round($value, 8);
|
||||
}
|
||||
|
||||
$html = view('form.amount-no-currency', compact('classes', 'name', 'label', 'value', 'options'))->render();
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param null $value
|
||||
|
||||
Reference in New Issue
Block a user