diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index bb1120606f..b4cd95d86d 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -25,13 +25,8 @@ 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\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionServiceTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; @@ -41,7 +36,7 @@ use FireflyIII\User; */ class RecurrenceFactory { - use TransactionTypeTrait, TransactionServiceTrait; + use TransactionTypeTrait, TransactionServiceTrait, RecurringTransactionTrait; /** @var User */ private $user; @@ -54,15 +49,16 @@ class RecurrenceFactory */ public function create(array $data): Recurrence { - $type = $this->findTransactionType(ucfirst($data['recurrence']['type'])); - $recurrence = new Recurrence( + $type = $this->findTransactionType(ucfirst($data['recurrence']['type'])); + $repetitions = (int)$data['recurrence']['repetitions']; + $recurrence = new Recurrence( [ 'user_id' => $this->user->id, 'transaction_type_id' => $type->id, 'title' => $data['recurrence']['title'], 'description' => $data['recurrence']['description'], 'first_date' => $data['recurrence']['first_date']->format('Y-m-d'), - 'repeat_until' => $data['recurrence']['repeat_until'], + 'repeat_until' => $repetitions > 0 ? null : $data['recurrence']['repeat_until'], 'latest_date' => null, 'repetitions' => $data['recurrence']['repetitions'], 'apply_rules' => $data['recurrence']['apply_rules'], @@ -71,105 +67,9 @@ class RecurrenceFactory ); $recurrence->save(); - // 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 - switch ($type->type) { - default: - throw new FireflyException(sprintf('Cannot create "%s".', $type->type)); - case TransactionType::WITHDRAWAL: - $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 - - $transaction = new RecurrenceTransaction( - [ - 'recurrence_id' => $recurrence->id, - 'transaction_currency_id' => $trArray['transaction_currency_id'], - 'foreign_currency_id' => '' === (string)$trArray['foreign_amount'] ? null : $trArray['foreign_currency_id'], - 'source_account_id' => $source->id, - 'destination_account_id' => $destination->id, - 'amount' => $trArray['amount'], - 'foreign_amount' => '' === (string)$trArray['foreign_amount'] ? null : (string)$trArray['foreign_amount'], - 'description' => $trArray['description'], - ] - ); - $transaction->save(); - - // 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'], - ] - ); - } - } + $this->updateMetaData($recurrence, $data); + $this->createRepetitions($recurrence, $data['repetitions'] ?? []); + $this->createTransactions($recurrence, $data['transactions'] ?? []); return $recurrence; } diff --git a/app/Http/Controllers/Recurring/CreateController.php b/app/Http/Controllers/Recurring/CreateController.php index c21ba97c63..3a75c998fc 100644 --- a/app/Http/Controllers/Recurring/CreateController.php +++ b/app/Http/Controllers/Recurring/CreateController.php @@ -71,7 +71,6 @@ class CreateController extends Controller */ public function create(Request $request) { - // todo create expandedform thing. $budgets = app('expandedform')->makeSelectListWithEmpty($this->budgets->getActiveBudgets()); $defaultCurrency = app('amount')->getDefaultCurrency(); $tomorrow = new Carbon; @@ -92,11 +91,12 @@ class CreateController extends Controller ]; // flash some data: + $hasOldInput = null !== $request->old('_token'); $preFilled = [ 'first_date' => $tomorrow->format('Y-m-d'), - 'transaction_type' => 'withdrawal', - 'active' => $request->old('active') ?? true, - 'apply_rules' => $request->old('apply_rules') ?? true, + 'transaction_type' => $hasOldInput ? $request->old('transaction_type') : 'withdrawal', + 'active' => $hasOldInput ? (bool)$request->old('active') : true, + 'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : true, ]; $request->session()->flash('preFilled', $preFilled); diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index 4088c85cd3..803ed91e62 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -25,6 +25,7 @@ namespace FireflyIII\Http\Controllers\Recurring; use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Http\Requests\RecurrenceFormRequest; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceRepetition; use FireflyIII\Repositories\Budget\BudgetRepositoryInterface; @@ -91,6 +92,13 @@ class EditController extends Controller if ('' !== $repetition->repetition_moment) { $currentRepetitionType .= ',' . $repetition->repetition_moment; } + + // put previous url in session if not redirect from store (not "return_to_edit"). + if (true !== session('recurrences.edit.fromUpdate')) { + $this->rememberPreviousUri('recurrences.edit.uri'); + } + $request->session()->forget('recurrences.edit.fromUpdate'); + // assume repeats forever: $repetitionEnd = 'forever'; // types of repetitions: @@ -112,12 +120,38 @@ class EditController extends Controller 'transaction_type' => strtolower($recurrence->transactionType->type), 'active' => $hasOldInput ? (bool)$request->old('active') : $recurrence->active, 'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules, - ]; $request->flash('preFilled', $preFilled); + return view('recurring.edit', compact('recurrence', 'array', 'budgets', 'preFilled', 'currentRepetitionType', 'repetitionEnd', 'repetitionEnds')); } + /** + * @param RecurrenceFormRequest $request + * @param Recurrence $recurrence + * + * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + * @throws \FireflyIII\Exceptions\FireflyException + */ + public function update(RecurrenceFormRequest $request, Recurrence $recurrence) + { + $data = $request->getAll(); + $this->recurring->update($recurrence, $data); + + $request->session()->flash('success', (string)trans('firefly.updated_recurrence', ['title' => $recurrence->title])); + app('preferences')->mark(); + + if (1 === (int)$request->get('return_to_edit')) { + // set value so edit routine will not overwrite URL: + $request->session()->put('recurrences.edit.fromUpdate', true); + + return redirect(route('recurring.edit', [$recurrence->id]))->withInput(['return_to_edit' => 1]); + } + + // redirect to previous URL. + return redirect($this->getPreviousUri('recurrences.edit.uri')); + } + } \ No newline at end of file diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index 79a32f02d9..1457ab4adc 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -33,7 +33,6 @@ use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; use FireflyIII\Transformers\RecurrenceTransformer; use Illuminate\Http\JsonResponse; use Illuminate\Http\Request; -use Response; use Symfony\Component\HttpFoundation\ParameterBag; /** @@ -86,7 +85,7 @@ class IndexController extends Controller // if $firstDate is beyond $end, simply return an empty array. if ($firstDate->gt($end)) { - return Response::json([]); + return response()->json([]); } // if $firstDate is beyond start, use that one: $actualStart = clone $firstDate; @@ -146,7 +145,7 @@ class IndexController extends Controller } } - return Response::json($return); + return response()->json($return); } @@ -168,10 +167,11 @@ class IndexController extends Controller $recurring = []; /** @var Recurrence $recurrence */ foreach ($collection as $recurrence) { - $array = $transformer->transform($recurrence); - $array['first_date'] = new Carbon($array['first_date']); - $array['latest_date'] = null === $array['latest_date'] ? null : new Carbon($array['latest_date']); - $recurring[] = $array; + $array = $transformer->transform($recurrence); + $array['first_date'] = new Carbon($array['first_date']); + $array['repeat_until'] = null === $array['repeat_until'] ? null : new Carbon($array['repeat_until']); + $array['latest_date'] = null === $array['latest_date'] ? null : new Carbon($array['latest_date']); + $recurring[] = $array; } return view('recurring.index', compact('recurring', 'page', 'pageSize')); @@ -207,10 +207,11 @@ class IndexController extends Controller */ public function suggest(Request $request): JsonResponse { - $today = new Carbon; - $date = Carbon::createFromFormat('Y-m-d', $request->get('date')); - $result = []; - if ($date > $today || $request->get('past') === 'true') { + $today = new Carbon; + $date = Carbon::createFromFormat('Y-m-d', $request->get('date')); + $preSelected = (string)$request->get('pre_select'); + $result = []; + if ($date > $today || (string)$request->get('past') === 'true') { $weekly = sprintf('weekly,%s', $date->dayOfWeekIso); $monthly = sprintf('monthly,%s', $date->day); $dayOfWeek = trans(sprintf('config.dow_%s', $date->dayOfWeekIso)); @@ -218,16 +219,16 @@ class IndexController extends Controller $yearly = sprintf('yearly,%s', $date->format('Y-m-d')); $yearlyDate = $date->formatLocalized(trans('config.month_and_day_no_year')); $result = [ - 'daily' => trans('firefly.recurring_daily'), - $weekly => trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]), - $monthly => trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]), - $ndom => trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $date->weekOfMonth]), - $yearly => trans('firefly.recurring_yearly', ['date' => $yearlyDate]), + 'daily' => ['label' => trans('firefly.recurring_daily'), 'selected' => 0 === strpos($preSelected, 'daily')], + $weekly => ['label' => trans('firefly.recurring_weekly', ['weekday' => $dayOfWeek]), 'selected' => 0 === strpos($preSelected, 'weekly')], + $monthly => ['label' => trans('firefly.recurring_monthly', ['dayOfMonth' => $date->day]), 'selected' => 0 === strpos($preSelected, 'monthly')], + $ndom => ['label' => trans('firefly.recurring_ndom', ['weekday' => $dayOfWeek, 'dayOfMonth' => $date->weekOfMonth]),'selected' => 0 === strpos($preSelected, 'ndom')], + $yearly => ['label' => trans('firefly.recurring_yearly', ['date' => $yearlyDate]),'selected' => 0 === strpos($preSelected, 'yearly')], ]; } - return Response::json($result); + return response()->json($result); } } \ No newline at end of file diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index 454e01af3d..ce6a2afe22 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -25,6 +25,7 @@ namespace FireflyIII\Http\Requests; use Carbon\Carbon; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Recurrence; use FireflyIII\Models\TransactionType; use FireflyIII\Rules\ValidRecurrenceRepetitionType; use FireflyIII\Rules\ValidRecurrenceRepetitionValue; @@ -53,14 +54,15 @@ class RecurrenceFormRequest extends Request $repetitionData = $this->parseRepetitionData(); $return = [ 'recurrence' => [ - 'type' => $this->string('transaction_type'), - 'title' => $this->string('title'), - 'description' => $this->string('recurring_description'), - 'first_date' => $this->date('first_date'), - 'repeat_until' => $this->date('repeat_until'), - 'repetitions' => $this->integer('repetitions'), - 'apply_rules' => $this->boolean('apply_rules'), - 'active' => $this->boolean('active'), + 'type' => $this->string('transaction_type'), + 'title' => $this->string('title'), + 'description' => $this->string('recurring_description'), + 'first_date' => $this->date('first_date'), + 'repeat_until' => $this->date('repeat_until'), + 'repetitions' => $this->integer('repetitions'), + 'apply_rules' => $this->boolean('apply_rules'), + 'active' => $this->boolean('active'), + 'repetition_end' => $this->string('repetition_end'), ], 'transactions' => [ [ @@ -77,7 +79,7 @@ class RecurrenceFormRequest extends Request ], 'meta' => [ // tags and piggy bank ID. - 'tags' => explode(',', $this->string('tags')), + 'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')): [], 'piggy_bank_id' => $this->integer('piggy_bank_id'), ], 'repetitions' => [ @@ -128,8 +130,7 @@ class RecurrenceFormRequest extends Request $tomorrow->addDay(); $rules = [ // mandatory info for recurrence. - //'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', - 'title' => 'required|between:1,255', + 'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', 'first_date' => 'required|date|after:' . $today->format('Y-m-d'), 'repetition_type' => ['required', new ValidRecurrenceRepetitionValue, new ValidRecurrenceRepetitionType, 'between:1,20'], 'skip' => 'required|numeric|between:0,31', @@ -196,6 +197,15 @@ class RecurrenceFormRequest extends Request throw new FireflyException(sprintf('Cannot handle transaction type of type "%s"', $this->string('transaction_type'))); // @codeCoverageIgnore } + // update some rules in case the user is editing a post: + /** @var Recurrence $recurrence */ + $recurrence = $this->route()->parameter('recurrence'); + if ($recurrence instanceof Recurrence) { + $rules['id'] = 'required|numeric|exists:recurrences,id'; + $rules['title'] = 'required|between:1,255|uniqueObjectForUser:recurrences,title,' . $recurrence->id; + $rules['first_date'] = 'required|date'; + } + return $rules; } diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index 7c7e08fd4d..a9ad116f02 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -45,6 +45,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property \FireflyIII\Models\Account $sourceAccount * @property \FireflyIII\Models\Account $destinationAccount * @property \Illuminate\Support\Collection $recurrenceTransactionMeta + * @property int $id */ class RecurrenceTransaction extends Model { diff --git a/app/Repositories/Recurring/RecurringRepository.php b/app/Repositories/Recurring/RecurringRepository.php index 57cce34211..3dccd78f6e 100644 --- a/app/Repositories/Recurring/RecurringRepository.php +++ b/app/Repositories/Recurring/RecurringRepository.php @@ -30,6 +30,7 @@ use FireflyIII\Models\Note; use FireflyIII\Models\Preference; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Services\Internal\Update\RecurrenceUpdateService; use FireflyIII\User; use Illuminate\Support\Collection; @@ -368,4 +369,20 @@ class RecurringRepository implements RecurringRepositoryInterface return $factory->create($data); } + + /** + * Update a recurring transaction. + * + * @param Recurrence $recurrence + * @param array $data + * + * @return Recurrence + */ + public function update(Recurrence $recurrence, array $data): Recurrence + { + /** @var RecurrenceUpdateService $service */ + $service = app(RecurrenceUpdateService::class); + + return $service->update($recurrence, $data); + } } \ No newline at end of file diff --git a/app/Repositories/Recurring/RecurringRepositoryInterface.php b/app/Repositories/Recurring/RecurringRepositoryInterface.php index 1770309800..53f9b63517 100644 --- a/app/Repositories/Recurring/RecurringRepositoryInterface.php +++ b/app/Repositories/Recurring/RecurringRepositoryInterface.php @@ -97,6 +97,8 @@ interface RecurringRepositoryInterface public function setUser(User $user): void; /** + * Store a new recurring transaction. + * * @param array $data * * @throws FireflyException @@ -104,4 +106,14 @@ interface RecurringRepositoryInterface */ public function store(array $data): Recurrence; + /** + * Update a recurring transaction. + * + * @param Recurrence $recurrence + * @param array $data + * + * @return Recurrence + */ + public function update(Recurrence $recurrence, array $data): Recurrence; + } \ No newline at end of file diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php new file mode 100644 index 0000000000..6495c35577 --- /dev/null +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -0,0 +1,190 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Internal\Support; + +use Exception; +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 Log; + + +/** + * Trait RecurringTransactionTrait + * + * @package FireflyIII\Services\Internal\Support + */ +trait RecurringTransactionTrait +{ + /** + * @param Recurrence $recurrence + * @param array $repetitions + */ + public function createRepetitions(Recurrence $recurrence, array $repetitions): void + { + /** @var array $array */ + foreach ($repetitions as $array) { + RecurrenceRepetition::create( + [ + 'recurrence_id' => $recurrence->id, + 'repetition_type' => $array['type'], + 'repetition_moment' => $array['moment'], + 'repetition_skip' => $array['skip'], + ] + ); + + } + } + + /** + * @param Recurrence $recurrence + * @param array $transactions + * + * @throws FireflyException + */ + public function createTransactions(Recurrence $recurrence, array $transactions): void + { + + foreach ($transactions as $array) { + $source = null; + $destination = null; + switch ($recurrence->transactionType->type) { + default: + throw new FireflyException(sprintf('Cannot create "%s".', $recurrence->transactionType->type)); + case TransactionType::WITHDRAWAL: + $source = $this->findAccount(AccountType::ASSET, $array['source_account_id'], null); + $destination = $this->findAccount(AccountType::EXPENSE, null, $array['destination_account_name']); + break; + case TransactionType::DEPOSIT: + $source = $this->findAccount(AccountType::REVENUE, null, $array['source_account_name']); + $destination = $this->findAccount(AccountType::ASSET, $array['destination_account_id'], null); + break; + case TransactionType::TRANSFER: + $source = $this->findAccount(AccountType::ASSET, $array['source_account_id'], null); + $destination = $this->findAccount(AccountType::ASSET, $array['destination_account_id'], null); + break; + } + + $transaction = new RecurrenceTransaction( + [ + 'recurrence_id' => $recurrence->id, + 'transaction_currency_id' => $array['transaction_currency_id'], + 'foreign_currency_id' => '' === (string)$array['foreign_amount'] ? null : $array['foreign_currency_id'], + 'source_account_id' => $source->id, + 'destination_account_id' => $destination->id, + 'amount' => $array['amount'], + 'foreign_amount' => '' === (string)$array['foreign_amount'] ? null : (string)$array['foreign_amount'], + 'description' => $array['description'], + ] + ); + $transaction->save(); + + // create recurrence transaction meta: + if ($array['budget_id'] > 0) { + RecurrenceTransactionMeta::create( + [ + 'rt_id' => $transaction->id, + 'name' => 'budget_id', + 'value' => $array['budget_id'], + ] + ); + } + if ('' !== (string)$array['category_name']) { + RecurrenceTransactionMeta::create( + [ + 'rt_id' => $transaction->id, + 'name' => 'category_name', + 'value' => $array['category_name'], + ] + ); + } + } + } + + /** + * @param Recurrence $recurrence + */ + public function deleteRepetitions(Recurrence $recurrence): void + { + $recurrence->recurrenceRepetitions()->delete(); + } + + /** + * @param Recurrence $recurrence + */ + public function deleteTransactions(Recurrence $recurrence): void + { + /** @var RecurrenceTransaction $transaction */ + foreach ($recurrence->recurrenceTransactions as $transaction) { + $transaction->recurrenceTransactionMeta()->delete(); + try { + $transaction->delete(); + } catch (Exception $e) { + Log::debug($e->getMessage()); + } + } + } + + /** + * @param Recurrence $recurrence + * @param array $data + */ + public function updateMetaData(Recurrence $recurrence, array $data): void + { + // only two special meta fields right now. Let's just hard code them. + $piggyId = (int)($data['meta']['piggy_bank_id'] ?? 0.0); + if ($piggyId > 0) { + /** @var RecurrenceMeta $entry */ + $entry = $recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->first(); + if (null === $entry) { + $entry = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'piggy_bank_id', 'value' => $piggyId]); + } + $entry->value = $piggyId; + $entry->save(); + } + if ($piggyId === 0) { + // delete if present + $recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->delete(); + } + $tags = $data['meta']['tags'] ?? []; + if (\count($tags) > 0) { + /** @var RecurrenceMeta $entry */ + $entry = $recurrence->recurrenceMeta()->where('name', 'tags')->first(); + if (null === $entry) { + $entry = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'tags', 'value' => implode(',', $tags)]); + } + $entry->value = implode(',', $tags); + $entry->save(); + } + if (\count($tags) === 0) { + // delete if present + $recurrence->recurrenceMeta()->where('name', 'tags')->delete(); + } + } +} \ No newline at end of file diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php new file mode 100644 index 0000000000..0c21caa630 --- /dev/null +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -0,0 +1,89 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Services\Internal\Update; + +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Recurrence; +use FireflyIII\Models\RecurrenceMeta; +use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; +use FireflyIII\Services\Internal\Support\TransactionServiceTrait; +use FireflyIII\Services\Internal\Support\TransactionTypeTrait; +use FireflyIII\User; + +/** + * Class RecurrenceUpdateService + */ +class RecurrenceUpdateService +{ + use TransactionTypeTrait, TransactionServiceTrait, RecurringTransactionTrait; + + /** @var User */ + private $user; + + /** + * Updates a recurrence. + * + * @param Recurrence $recurrence + * @param array $data + * + * @return Recurrence + * @throws FireflyException + */ + public function update(Recurrence $recurrence, array $data): Recurrence + { + // is expected by TransactionServiceTrait + $this->user = $recurrence->user; + $transactionType = $this->findTransactionType(ucfirst($data['recurrence']['type'])); + // update basic fields first: + $recurrence->transaction_type_id = $transactionType->id; + $recurrence->title = $data['recurrence']['title'] ?? $recurrence->title; + $recurrence->description = $data['recurrence']['description'] ?? $recurrence->description; + $recurrence->first_date = $data['recurrence']['first_date'] ?? $recurrence->first_date; + $recurrence->repeat_until = $data['recurrence']['repeat_until'] ?? $recurrence->repeat_until; + $recurrence->repetitions = $data['recurrence']['repetitions'] ?? $recurrence->repetitions; + $recurrence->apply_rules = $data['recurrence']['apply_rules'] ?? $recurrence->apply_rules; + $recurrence->active = $data['recurrence']['active'] ?? $recurrence->active; + + if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'until_date'])) { + $recurrence->repetitions = 0; + } + if (\in_array($data['recurrence']['repetition_end'], ['forever ', 'times'])) { + $recurrence->repeat_until = null; + } + $recurrence->save(); + + // update all meta data: + $this->updateMetaData($recurrence, $data); + + // update all repetitions + $this->deleteRepetitions($recurrence); + $this->createRepetitions($recurrence, $data['repetitions'] ?? []); + + // update all transactions (and associated meta-data); + $this->deleteTransactions($recurrence); + $this->createTransactions($recurrence, $data['transactions'] ?? []); + + return $recurrence; + } +} \ No newline at end of file diff --git a/public/js/ff/recurring/create.js b/public/js/ff/recurring/create.js index c3a3580734..d8cbe8912f 100644 --- a/public/js/ff/recurring/create.js +++ b/public/js/ff/recurring/create.js @@ -104,7 +104,14 @@ function respondToFirstDateChange() { var select = $('#ffInput_repetition_type'); var date = obj.val(); select.prop('disabled', true); - $.getJSON(suggestUri, {date: date}).fail(function () { + + // preselected value: + var preSelected = oldRepetitionType; + if(preSelected === '') { + preSelected = select.val(); + } + + $.getJSON(suggestUri, {date: date,pre_select: preSelected}).fail(function () { console.error('Could not load repetition suggestions'); alert('Could not load repetition suggestions'); }).done(parseRepetitionSuggestions); @@ -117,8 +124,9 @@ function parseRepetitionSuggestions(data) { var opt; for (var k in data) { if (data.hasOwnProperty(k)) { - opt = $('').val(k).attr('label', data[k]).text(data[k]); - if(k === oldRepetitionType) { + console.log('label: ' + data[k].label + ', selected: ' + data[k].selected); + opt = $('').val(k).attr('label', data[k].label).text(data[k].label); + if(data[k].selected) { opt.attr('selected','selected'); } select.append(opt); diff --git a/resources/lang/en_US/firefly.php b/resources/lang/en_US/firefly.php index ab709109db..e2020d6728 100644 --- a/resources/lang/en_US/firefly.php +++ b/resources/lang/en_US/firefly.php @@ -1247,4 +1247,9 @@ return [ 'store_new_recurrence' => 'Store recurring transaction', 'stored_new_recurrence' => 'Recurring transaction ":title" stored successfully.', 'edit_recurrence' => 'Edit recurring transaction ":title"', + 'recurring_repeats_until' => 'Repeats until :date', + 'recurring_repeats_forever' => 'Repeats forever', + 'recurring_repeats_x_times' => 'Repeats :count time(s)', + 'update_recurrence' => 'Update recurring transaction', + 'updated_recurrence' => 'Updated recurring transaction ":title"', ]; diff --git a/resources/views/recurring/edit.twig b/resources/views/recurring/edit.twig index a1f3c15876..50a06ef38d 100644 --- a/resources/views/recurring/edit.twig +++ b/resources/views/recurring/edit.twig @@ -4,7 +4,7 @@ {% endblock %} {% block content %} - + {{ Form.model(recurrence, {'class' : 'form-horizontal','enctype': 'multipart/form-data','id' : 'update','url' : route('recurring.update', recurrence.id)}) }} {# row with recurrence information #} @@ -16,7 +16,8 @@ {{ 'mandatory_for_recurring'|_ }} - {{ ExpandedForm.text('title',array.title) }} + + {{ ExpandedForm.text('title') }} {{ ExpandedForm.date('first_date',array.first_date, {helpText: trans('firefly.help_first_date_no_past')}) }} {{ ExpandedForm.select('repetition_type', [], null, {helpText: trans('firefly.change_date_other_options')}) }} {{ ExpandedForm.number('skip', array.recurrence_repetitions[0].repetition_skip) }} @@ -48,9 +49,8 @@ {{ ExpandedForm.textarea('recurring_description',array.description) }} {# only correct way to do active checkbox #} - {{ ExpandedForm.checkbox('active', 1) }} - - {{ ExpandedForm.checkbox('apply_rules',1) }} + {{ ExpandedForm.checkbox('active', 1, preFilled.active) }} + {{ ExpandedForm.checkbox('apply_rules', 1, preFilled.apply_rules) }} @@ -162,11 +162,11 @@ {{ 'options'|_ }} - {{ ExpandedForm.optionsList('create','recurrence') }} + {{ ExpandedForm.optionsList('update','recurrence') }} diff --git a/resources/views/recurring/index.twig b/resources/views/recurring/index.twig index 6cc5a4252f..60f0e303fd 100644 --- a/resources/views/recurring/index.twig +++ b/resources/views/recurring/index.twig @@ -94,6 +94,17 @@ {% endfor %} + + {% if null == rt.repeat_until and rt.repetitions == 0 %} + {{ 'recurring_repeats_forever'|_ }} + {% endif %} + {% if null != rt.repeat_until and rt.repetitions == 0 %} + {{ trans('firefly.recurring_repeats_until', {date: rt.repeat_until.formatLocalized(monthAndDayFormat)}) }} + {% endif %} + {% if null == rt.repeat_until and rt.repetitions != 0 %} + {{ trans('firefly.recurring_repeats_x_times', {count: rt.repetitions}) }} + {% endif %} + {% endfor %} diff --git a/routes/web.php b/routes/web.php index d1086b42b7..fc5461766d 100755 --- a/routes/web.php +++ b/routes/web.php @@ -625,7 +625,7 @@ Route::group( Route::get('delete/{recurrence}', ['uses' => 'DeleteController@delete', 'as' => 'delete']); Route::post('store', ['uses' => 'CreateController@store', 'as' => 'store']); - Route::post('update', ['uses' => 'EditController@update', 'as' => 'update']); + Route::post('update/{recurrence}', ['uses' => 'EditController@update', 'as' => 'update']); } );
+ {% if null == rt.repeat_until and rt.repetitions == 0 %} + {{ 'recurring_repeats_forever'|_ }} + {% endif %} + {% if null != rt.repeat_until and rt.repetitions == 0 %} + {{ trans('firefly.recurring_repeats_until', {date: rt.repeat_until.formatLocalized(monthAndDayFormat)}) }} + {% endif %} + {% if null == rt.repeat_until and rt.repetitions != 0 %} + {{ trans('firefly.recurring_repeats_x_times', {count: rt.repetitions}) }} + {% endif %} +