From d73cd4b515da0de4b8ec60e9fe53c85fdab60210 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 17 Jun 2018 15:14:34 +0200 Subject: [PATCH] Can now create recurring transactions. #1469 --- app/Factory/RecurrenceFactory.php | 87 ++++++- .../Recurring/CreateController.php | 49 ++-- .../Controllers/Recurring/EditController.php | 22 +- .../Controllers/Recurring/IndexController.php | 10 +- .../Transaction/SingleController.php | 15 +- app/Http/Requests/RecurrenceFormRequest.php | 53 +++- app/Models/Recurrence.php | 19 +- app/Models/RecurrenceMeta.php | 12 + app/Models/RecurrenceRepetition.php | 12 + app/Models/RecurrenceTransaction.php | 10 + app/Models/RecurrenceTransactionMeta.php | 13 +- app/Rules/ValidRecurrenceRepetitionValue.php | 107 ++++++++ app/Support/ExpandedForm.php | 30 ++- config/twigbridge.php | 3 +- public/js/ff/recurring/create.js | 7 +- public/js/ff/recurring/edit.js | 237 ++++++++++++++++++ public/js/ff/transactions/single/create.js | 2 +- resources/lang/en_US/firefly.php | 3 + resources/views/partials/menu-sidebar.twig | 2 +- resources/views/recurring/create.twig | 3 +- resources/views/recurring/edit.twig | 214 ++++++++++++++++ resources/views/recurring/index.twig | 3 +- .../views/transactions/single/create.twig | 4 +- routes/breadcrumbs.php | 8 + routes/web.php | 1 + 25 files changed, 852 insertions(+), 74 deletions(-) create mode 100644 app/Rules/ValidRecurrenceRepetitionValue.php create mode 100644 public/js/ff/recurring/edit.js create mode 100644 resources/views/recurring/edit.twig diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index cb34751236..bb1120606f 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -27,7 +27,10 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\AccountType; use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceMeta; +use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Models\RecurrenceTransaction; +use FireflyIII\Models\RecurrenceTransactionMeta; use FireflyIII\Models\TransactionType; use FireflyIII\Services\Internal\Support\TransactionServiceTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; @@ -51,9 +54,6 @@ class RecurrenceFactory */ public function create(array $data): Recurrence { - echo '
';
-        print_r($data);
-        echo '
'; $type = $this->findTransactionType(ucfirst($data['recurrence']['type'])); $recurrence = new Recurrence( [ @@ -70,10 +70,49 @@ class RecurrenceFactory ] ); $recurrence->save(); - var_dump($recurrence->toArray()); - // create transactions + // create recurrence meta (tags) + if (\count($data['meta']['tags']) > 0) { + // todo move to factory + $tags = implode(',', $data['meta']['tags']); + if ('' !== $tags) { + $metaValue = RecurrenceMeta::create( + [ + 'recurrence_id' => $recurrence->id, + 'name' => 'tags', + 'value' => $tags, + ] + ); + } + } + // create recurrence meta (piggy bank ID): + if ($data['meta']['piggy_bank_id'] > 0) { + // todo move to factory + $metaValue = RecurrenceMeta::create( + [ + 'recurrence_id' => $recurrence->id, + 'name' => 'piggy_bank_id', + 'value' => $data['meta']['piggy_bank_id'], + ] + ); + } + + // store recurrence repetitions: + foreach ($data['repetitions'] as $repArray) { + // todo move to factory + $repetition = RecurrenceRepetition::create( + [ + 'recurrence_id' => $recurrence->id, + 'repetition_type' => $repArray['type'], + 'repetition_moment' => $repArray['moment'], + 'repetition_skip' => $repArray['skip'], + ] + ); + } + + // create recurrence transactions foreach ($data['transactions'] as $trArray) { + // todo move to factory $source = null; $destination = null; // search source account, depends on type @@ -84,6 +123,14 @@ class RecurrenceFactory $source = $this->findAccount(AccountType::ASSET, $trArray['source_account_id'], null); $destination = $this->findAccount(AccountType::EXPENSE, null, $trArray['destination_account_name']); break; + case TransactionType::DEPOSIT: + $source = $this->findAccount(AccountType::REVENUE, null, $trArray['source_account_name']); + $destination = $this->findAccount(AccountType::ASSET, $trArray['destination_account_id'], null); + break; + case TransactionType::TRANSFER: + $source = $this->findAccount(AccountType::ASSET, $trArray['source_account_id'], null); + $destination = $this->findAccount(AccountType::ASSET, $trArray['destination_account_id'], null); + break; } // search destination account @@ -101,16 +148,30 @@ class RecurrenceFactory ] ); $transaction->save(); - var_dump($transaction->toArray()); + + // create recurrence transaction meta: + // todo move to factory + if ($trArray['budget_id'] > 0) { + $trMeta = RecurrenceTransactionMeta::create( + [ + 'rt_id' => $transaction->id, + 'name' => 'budget_id', + 'value' => $trArray['budget_id'], + ] + ); + } + if ('' !== (string)$trArray['category_name']) { + $trMeta = RecurrenceTransactionMeta::create( + [ + 'rt_id' => $transaction->id, + 'name' => 'category_name', + 'value' => $trArray['category_name'], + ] + ); + } } - // create meta data: - if(\count($data['meta']['tags']) > 0) { - // todo store tags - } - - exit; - + return $recurrence; } /** diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index c2c477b883..7dffe39973 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -28,7 +28,6 @@ use Carbon\Carbon; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Requests\RecurrenceFormRequest; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; -use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use Illuminate\Http\Request; @@ -40,8 +39,6 @@ class CreateController extends Controller { /** @var BudgetRepositoryInterface */ private $budgets; - /** @var PiggyBankRepositoryInterface */ - private $piggyBanks; /** @var RecurringRepositoryInterface */ private $recurring; @@ -59,9 +56,8 @@ class CreateController extends Controller app('view')->share('title', trans('firefly.recurrences')); app('view')->share('subTitle', trans('firefly.create_new_recurrence')); - $this->recurring = app(RecurringRepositoryInterface::class); - $this->budgets = app(BudgetRepositoryInterface::class); - $this->piggyBanks = app(PiggyBankRepositoryInterface::class); + $this->recurring = app(RecurringRepositoryInterface::class); + $this->budgets = app(BudgetRepositoryInterface::class); return $next($request); } @@ -75,14 +71,18 @@ class CreateController extends Controller */ public function create(Request $request) { - // todo refactor to expandedform method. - $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets()); - $defaultCurrency = app('amount')->getDefaultCurrency(); - $piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount(); - $piggies = app('expandedform')->makeSelectListWithEmpty($piggyBanks); - $tomorrow = new Carbon; + $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets()); + $defaultCurrency = app('amount')->getDefaultCurrency(); + $tomorrow = new Carbon; + $oldRepetitionType = $request->old('repetition_type'); $tomorrow->addDay(); + // put previous url in session if not redirect from store (not "create another"). + if (true !== session('recurring.create.fromStore')) { + $this->rememberPreviousUri('recurring.create.uri'); + } + $request->session()->forget('recurring.create.fromStore'); + // types of repetitions: $typesOfRepetitions = [ 'forever' => trans('firefly.repeat_forever'), @@ -99,18 +99,33 @@ class CreateController extends Controller ]; $request->session()->flash('preFilled', $preFilled); - return view('recurring.create', compact('tomorrow', 'preFilled', 'piggies', 'typesOfRepetitions', 'defaultCurrency', 'budgets')); + return view('recurring.create', compact('tomorrow', 'oldRepetitionType', 'preFilled', 'piggies', 'typesOfRepetitions', 'defaultCurrency', 'budgets')); } /** * @param RecurrenceFormRequest $request + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws \FireflyIII\Exceptions\FireflyException */ public function store(RecurrenceFormRequest $request) { - $data = $request->getAll(); - $this->recurring->store($data); - var_dump($data); - exit; + $data = $request->getAll(); + $recurrence = $this->recurring->store($data); + + $request->session()->flash('success', (string)trans('firefly.stored_new_recurrence', ['title' => $recurrence->title])); + app('preferences')->mark(); + + if (1 === (int)$request->get('create_another')) { + // set value so create routine will not overwrite URL: + $request->session()->put('recurring.create.fromStore', true); + + return redirect(route('recurring.create'))->withInput(); + } + + // redirect to previous URL. + return redirect($this->getPreviousUri('recurring.create.uri')); + } } \ No newline at end of file diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index 6ef2b0beb2..9107e472e0 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -26,6 +26,8 @@ namespace FireflyIII\Http\Controllers\Recurring; use FireflyIII\Http\Controllers\Controller; use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; /** * @@ -33,6 +35,9 @@ use FireflyIII\Models\Recurrence; */ class EditController extends Controller { + /** @var RecurringRepositoryInterface */ + private $recurring; + /** * */ @@ -56,10 +61,23 @@ class EditController extends Controller /** * @param Recurrence $recurrence + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View */ - public function edit(Recurrence $recurrence) { + public function edit(Recurrence $recurrence) + { + // get recurrence type: + // todo move to repository + /** @var RecurrenceRepetition $repetition */ + $repetition = $recurrence->recurrenceRepetitions()->first(); + $currentRepetitionType = $repetition->repetition_type; + if ('' !== $repetition->repetition_moment) { + $currentRepetitionType .= ',' . $repetition->repetition_moment; + } - return view('recurring.edit', compact('recurrence')); + // todo handle old repetition type as well. + + return view('recurring.edit', compact('recurrence','currentRepetitionType')); } diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index 810000d2fc..fd3b1c584b 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -71,7 +71,7 @@ class IndexController extends Controller * @throws FireflyException * @return JsonResponse */ - function events(RecurringRepositoryInterface $repository, Request $request): JsonResponse + function events(Request $request): JsonResponse { $return = []; $start = Carbon::createFromFormat('Y-m-d', $request->get('start')); @@ -118,14 +118,14 @@ class IndexController extends Controller throw new FireflyException(sprintf('Cannot generate events for type that ends at "%s".', $endsAt)); case 'forever': // simply generate up until $end. No change from default behavior. - $occurrences = $repository->getOccurrencesInRange($repetition, $actualStart, $actualEnd); + $occurrences = $this->recurring->getOccurrencesInRange($repetition, $actualStart, $actualEnd); break; case 'until_date': $actualEnd = $endDate ?? clone $end; - $occurrences = $repository->getOccurrencesInRange($repetition, $actualStart, $actualEnd); + $occurrences = $this->recurring->getOccurrencesInRange($repetition, $actualStart, $actualEnd); break; case 'times': - $occurrences = $repository->getXOccurrences($repetition, $actualStart, $repetitions); + $occurrences = $this->recurring->getXOccurrences($repetition, $actualStart, $repetitions); break; } @@ -210,7 +210,7 @@ class IndexController extends Controller $today = new Carbon; $date = Carbon::createFromFormat('Y-m-d', $request->get('date')); $result = []; - if ($date > $today) { + if ($date > $today || $request->get('past') === 'true') { $weekly = sprintf('weekly,%s', $date->dayOfWeekIso); $monthly = sprintf('monthly,%s', $date->day); $dayOfWeek = trans(sprintf('config.dow_%s', $date->dayOfWeekIso)); diff --git a/app/Http/Controllers/Transaction/SingleController.php b/app/Http/Controllers/Transaction/SingleController.php index af415c0ee8..c1b22258c1 100644 --- a/app/Http/Controllers/Transaction/SingleController.php +++ b/app/Http/Controllers/Transaction/SingleController.php @@ -50,14 +50,8 @@ class SingleController extends Controller { /** @var AttachmentHelperInterface */ private $attachments; - /** @var BudgetRepositoryInterface */ private $budgets; - /** @var CurrencyRepositoryInterface */ - private $currency; - /** @var PiggyBankRepositoryInterface */ - private $piggyBanks; - /** @var JournalRepositoryInterface */ private $repository; @@ -77,9 +71,7 @@ class SingleController extends Controller $this->middleware( function ($request, $next) { $this->budgets = app(BudgetRepositoryInterface::class); - $this->piggyBanks = app(PiggyBankRepositoryInterface::class); $this->attachments = app(AttachmentHelperInterface::class); - $this->currency = app(CurrencyRepositoryInterface::class); $this->repository = app(JournalRepositoryInterface::class); app('view')->share('title', trans('firefly.transactions')); @@ -101,7 +93,6 @@ class SingleController extends Controller $destination = $this->repository->getJournalDestinationAccounts($journal)->first(); $budgetId = $this->repository->getJournalBudgetId($journal); $categoryName = $this->repository->getJournalCategoryName($journal); - $tags = implode(',', $this->repository->getTags($journal)); /** @var Transaction $transaction */ $transaction = $journal->transactions()->first(); @@ -156,8 +147,6 @@ class SingleController extends Controller $what = strtolower($what); $what = $request->old('what') ?? $what; $budgets = ExpandedForm::makeSelectListWithEmpty($this->budgets->getActiveBudgets()); - $piggyBanks = $this->piggyBanks->getPiggyBanksWithAmount(); - $piggies = ExpandedForm::makeSelectListWithEmpty($piggyBanks); $preFilled = session()->has('preFilled') ? session('preFilled') : []; $subTitle = trans('form.add_new_' . $what); $subTitleIcon = 'fa-plus'; @@ -179,11 +168,9 @@ class SingleController extends Controller } session()->forget('transactions.create.fromStore'); - asort($piggies); - return view( 'transactions.single.create', - compact('subTitleIcon', 'budgets', 'what', 'piggies', 'subTitle', 'optionalFields', 'preFilled') + compact('subTitleIcon', 'budgets', 'what', 'subTitle', 'optionalFields', 'preFilled') ); } diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index f87a32295d..19c0f47ccb 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -27,6 +27,7 @@ use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\TransactionType; use FireflyIII\Rules\ValidRecurrenceRepetitionType; +use FireflyIII\Rules\ValidRecurrenceRepetitionValue; /** * Class RecurrenceFormRequest @@ -49,8 +50,8 @@ class RecurrenceFormRequest extends Request */ public function getAll(): array { - $data = $this->all(); - $return = [ + $repetitionData = $this->parseRepetitionData(); + $return = [ 'recurrence' => [ 'type' => $this->string('transaction_type'), 'title' => $this->string('title'), @@ -81,7 +82,9 @@ class RecurrenceFormRequest extends Request ], 'repetitions' => [ [ - 'skip' => $this->integer('skip'), + 'type' => $repetitionData['type'], + 'moment' => $repetitionData['moment'], + 'skip' => $this->integer('skip'), ], ], @@ -101,6 +104,14 @@ class RecurrenceFormRequest extends Request $return['transactions'][0]['source_account_id'] = $this->integer('source_account_id'); $return['transactions'][0]['destination_account_name'] = $this->string('destination_account_name'); break; + case 'deposit': + $return['transactions'][0]['source_account_name'] = $this->string('source_account_name'); + $return['transactions'][0]['destination_account_id'] = $this->integer('destination_account_id'); + break; + case 'transfer': + $return['transactions'][0]['source_account_id'] = $this->integer('source_account_id'); + $return['transactions'][0]['destination_account_id'] = $this->integer('destination_account_id'); + break; } return $return; @@ -120,7 +131,7 @@ class RecurrenceFormRequest extends Request //'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', 'title' => 'required|between:1,255', 'first_date' => 'required|date|after:' . $today->format('Y-m-d'), - 'repetition_type' => ['required', new ValidRecurrenceRepetitionType, 'between:1,20'], + 'repetition_type' => ['required', new ValidRecurrenceRepetitionValue, new ValidRecurrenceRepetitionType, 'between:1,20'], 'skip' => 'required|numeric|between:0,31', // optional for recurrence: @@ -186,4 +197,38 @@ class RecurrenceFormRequest extends Request return $rules; } + + /** + * @return array + */ + private function parseRepetitionData(): array + { + $value = $this->string('repetition_type'); + $return = [ + 'type' => '', + 'moment' => '', + ]; + + if ($value === 'daily') { + $return['type'] = $value; + } + //monthly,17 + //ndom,3,7 + if (\in_array(substr($value, 0, 6), ['yearly', 'weekly'])) { + $return['type'] = substr($value, 0, 6); + $return['moment'] = substr($value, 7); + } + if (0 === strpos($value, 'monthly')) { + $return['type'] = substr($value, 0, 7); + $return['moment'] = substr($value, 8); + } + if (0 === strpos($value, 'ndom')) { + $return['type'] = substr($value, 0, 4); + $return['moment'] = substr($value, 5); + } + + return $return; + + + } } \ No newline at end of file diff --git a/app/Models/Recurrence.php b/app/Models/Recurrence.php index b3ce26d0f3..521b0bd673 100644 --- a/app/Models/Recurrence.php +++ b/app/Models/Recurrence.php @@ -65,14 +65,19 @@ class Recurrence extends Model */ protected $casts = [ - 'created_at' => 'datetime', - 'updated_at' => 'datetime', - 'first_date' => 'date', - 'latest_date' => 'date', - 'active' => 'bool', - 'apply_rules' => 'bool', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'title' => 'string', + 'description' => 'string', + 'first_date' => 'date', + 'repeat_until' => 'date', + 'latest_date' => 'date', + 'repetitions' => 'int', + 'active' => 'bool', + 'apply_rules' => 'bool', ]; - /** @var array */ + /** @var array */ protected $fillable = ['user_id', 'transaction_type_id', 'title', 'description', 'first_date', 'repeat_until', 'latest_date', 'repetitions', 'apply_rules', 'active']; /** @var string */ diff --git a/app/Models/RecurrenceMeta.php b/app/Models/RecurrenceMeta.php index a448e015de..1f4a64914b 100644 --- a/app/Models/RecurrenceMeta.php +++ b/app/Models/RecurrenceMeta.php @@ -35,6 +35,18 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; */ class RecurrenceMeta extends Model { + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'name' => 'string', + 'value' => 'string', + ]; + /** @var array */ + protected $fillable = ['recurrence_id', 'name', 'value']; + /** @var string */ protected $table = 'recurrences_meta'; /** diff --git a/app/Models/RecurrenceRepetition.php b/app/Models/RecurrenceRepetition.php index 64a28e1309..c1331a83dd 100644 --- a/app/Models/RecurrenceRepetition.php +++ b/app/Models/RecurrenceRepetition.php @@ -40,6 +40,18 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; */ class RecurrenceRepetition extends Model { + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'repetition_type' => 'string', + 'repetition_moment' => 'string', + 'repetition_skip' => 'int', + ]; + protected $fillable = ['recurrence_id', 'repetition_type', 'repetition_moment', 'repetition_skip']; + /** @var string */ protected $table = 'recurrences_repetitions'; /** diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index 7d729c8fdc..e0da906860 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -48,6 +48,16 @@ use Illuminate\Database\Eloquent\Relations\HasMany; class RecurrenceTransaction extends Model { /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'amount' => 'string', + 'foreign_amount' => 'string', + 'description' => 'string', + ]; + /** @var array */ protected $fillable = ['recurrence_id', 'transaction_currency_id', 'foreign_currency_id', 'source_account_id', 'destination_account_id', 'amount', 'foreign_amount', 'description']; diff --git a/app/Models/RecurrenceTransactionMeta.php b/app/Models/RecurrenceTransactionMeta.php index 3c9d04a730..0072c1764f 100644 --- a/app/Models/RecurrenceTransactionMeta.php +++ b/app/Models/RecurrenceTransactionMeta.php @@ -35,6 +35,17 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; */ class RecurrenceTransactionMeta extends Model { + /** @var array */ + protected $casts + = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + 'name' => 'string', + 'value' => 'string', + ]; + protected $fillable = ['rt_id', 'name', 'value']; + /** @var string */ protected $table = 'rt_meta'; /** @@ -43,7 +54,7 @@ class RecurrenceTransactionMeta extends Model */ public function recurrenceTransaction(): BelongsTo { - return $this->belongsTo(RecurrenceTransaction::class); + return $this->belongsTo(RecurrenceTransaction::class, 'rt_id'); } } \ No newline at end of file diff --git a/app/Rules/ValidRecurrenceRepetitionValue.php b/app/Rules/ValidRecurrenceRepetitionValue.php new file mode 100644 index 0000000000..3e2afe32a5 --- /dev/null +++ b/app/Rules/ValidRecurrenceRepetitionValue.php @@ -0,0 +1,107 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Rules; + + +use Carbon\Carbon; +use Illuminate\Contracts\Validation\Rule; +use InvalidArgumentException; + +/** + * Class ValidRecurrenceRepetitionValue + */ +class ValidRecurrenceRepetitionValue implements Rule +{ + + /** + * Get the validation error message. + * + * @return string + */ + public function message(): string + { + return trans('validation.valid_recurrence_rep_type'); + } + + /** + * Determine if the validation rule passes. + * + * @param string $attribute + * @param mixed $value + * + * @return bool + */ + public function passes($attribute, $value): bool + { + $value = (string)$value; + + if ($value === 'daily') { + return true; + } + + if (0 === strpos($value, 'monthly')) { + $dayOfMonth = (int)substr($value, 8); + + return $dayOfMonth > 0 && $dayOfMonth < 32; + } + + //ndom,3,7 + // nth x-day of the month. + if (0 === strpos($value, 'ndom')) { + $parameters = explode(',', substr($value, 5)); + if (\count($parameters) !== 2) { + return false; + } + $nthDay = (int)($parameters[0] ?? 0.0); + $dayOfWeek = (int)($parameters[1] ?? 0.0); + if ($nthDay < 1 || $nthDay > 5) { + return false; + } + + return $dayOfWeek > 0 && $dayOfWeek < 8; + } + + //weekly,7 + if (0 === strpos($value, 'weekly')) { + $dayOfWeek = (int)substr($value, 7); + + return $dayOfWeek > 0 && $dayOfWeek < 8; + } + + //yearly,2018-01-01 + if (0 === strpos($value, 'yearly')) { + // rest of the string must be valid date: + $dateString = substr($value, 7); + try { + $date = Carbon::createFromFormat('Y-m-d', $dateString); + } catch (InvalidArgumentException $e) { + return false; + } + + return true; + } + + return false; + } +} \ No newline at end of file diff --git a/app/Support/ExpandedForm.php b/app/Support/ExpandedForm.php index 17467594c0..a25c51b383 100644 --- a/app/Support/ExpandedForm.php +++ b/app/Support/ExpandedForm.php @@ -27,10 +27,12 @@ use Carbon\Carbon; use Eloquent; use FireflyIII\Models\Account; use FireflyIII\Models\AccountType; +use FireflyIII\Models\PiggyBank; use FireflyIII\Models\RuleGroup; use FireflyIII\Models\TransactionCurrency; use FireflyIII\Repositories\Account\AccountRepositoryInterface; use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface; +use FireflyIII\Repositories\PiggyBank\PiggyBankRepositoryInterface; use FireflyIII\Repositories\RuleGroup\RuleGroupRepositoryInterface; use Form; use Illuminate\Support\Collection; @@ -183,6 +185,7 @@ class ExpandedForm * * @return string * @throws \FireflyIII\Exceptions\FireflyException + * @throws \Throwable */ public function balance(string $name, $value = null, array $options = []): string { @@ -506,8 +509,9 @@ class ExpandedForm * @return string * @throws \Throwable */ - public function password(string $name, array $options = []): string + public function password(string $name, array $options = null): string { + $options = $options ?? []; $label = $this->label($name, $options); $options = $this->expandOptionArray($name, $label, $options); $classes = $this->getHolderClasses($name); @@ -516,6 +520,30 @@ class ExpandedForm return $html; } + /** + * @param string $name + * @param null $value + * @param array $options + * + * @return string + * @throws \Throwable + */ + public function piggyBankList(string $name, $value = null, array $options = null): string + { + $options = $options ?? []; + // make repositories + /** @var PiggyBankRepositoryInterface $repository */ + $repository = app(PiggyBankRepositoryInterface::class); + $piggyBanks = $repository->getPiggyBanksWithAmount(); + $array = []; + /** @var PiggyBank $piggy */ + foreach ($piggyBanks as $piggy) { + $array[$piggy->id] = $piggy->name; + } + + return $this->select($name, $array, $value, $options); + } + /** * @param string $name * @param null $value diff --git a/config/twigbridge.php b/config/twigbridge.php index f4a2c2167a..c5c5dc98f0 100644 --- a/config/twigbridge.php +++ b/config/twigbridge.php @@ -188,7 +188,8 @@ return [ 'is_safe' => [ 'date', 'text', 'select', 'balance', 'optionsList', 'checkbox', 'amount', 'tags', 'integer', 'textarea', 'location', 'multiRadio', 'file', 'multiCheckbox', 'staticText', 'amountSmall', 'password', 'nonSelectableBalance', 'nonSelectableAmount', - 'number', 'assetAccountList','amountNoCurrency','currencyList','ruleGroupList','assetAccountCheckList','ruleGroupListWithEmpty' + 'number', 'assetAccountList','amountNoCurrency','currencyList','ruleGroupList','assetAccountCheckList','ruleGroupListWithEmpty', + 'piggyBankList' ], ], 'Form' => [ diff --git a/public/js/ff/recurring/create.js b/public/js/ff/recurring/create.js index a2b8140c65..c3a3580734 100644 --- a/public/js/ff/recurring/create.js +++ b/public/js/ff/recurring/create.js @@ -114,9 +114,14 @@ function parseRepetitionSuggestions(data) { var select = $('#ffInput_repetition_type'); select.empty(); + var opt; for (var k in data) { if (data.hasOwnProperty(k)) { - select.append($('