diff --git a/app/Http/Controllers/Recurring/EditController.php b/app/Http/Controllers/Recurring/EditController.php index e158cdd51c..acbc5a7a64 100644 --- a/app/Http/Controllers/Recurring/EditController.php +++ b/app/Http/Controllers/Recurring/EditController.php @@ -127,9 +127,10 @@ class EditController extends Controller 'apply_rules' => $hasOldInput ? (bool)$request->old('apply_rules') : $recurrence->apply_rules, 'deposit_source_id' => $array['transactions'][0]['source_id'], 'withdrawal_destination_id' => $array['transactions'][0]['destination_id'], - ]; + $array['transactions'][0]['tags'] = implode(',', $array['transactions'][0]['tags']); + return view( 'recurring.edit', compact('recurrence', 'array', 'weekendResponses', 'budgets', 'preFilled', 'currentRepType', 'repetitionEnd', 'repetitionEnds') @@ -148,6 +149,7 @@ class EditController extends Controller 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])); diff --git a/app/Http/Controllers/Recurring/IndexController.php b/app/Http/Controllers/Recurring/IndexController.php index c3acc0336b..544d54cfe6 100644 --- a/app/Http/Controllers/Recurring/IndexController.php +++ b/app/Http/Controllers/Recurring/IndexController.php @@ -68,6 +68,7 @@ class IndexController extends Controller } /** + * TODO the notes of a recurrence are pretty pointless at this moment. * Show all recurring transactions. * * @param Request $request @@ -108,33 +109,4 @@ class IndexController extends Controller return view('recurring.index', compact('paginator', 'page', 'pageSize', 'total')); } - /** - * Show a single recurring transaction. - * - * @param Recurrence $recurrence - * - * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View - * @throws FireflyException - */ - public function show(Recurrence $recurrence) - { - /** @var RecurrenceTransformer $transformer */ - $transformer = app(RecurrenceTransformer::class); - $transformer->setParameters(new ParameterBag); - - $array = $transformer->transform($recurrence); - $groups = $this->recurring->getTransactions($recurrence); - - // transform dates back to Carbon objects: - foreach ($array['recurrence_repetitions'] as $index => $repetition) { - foreach ($repetition['occurrences'] as $item => $occurrence) { - $array['recurrence_repetitions'][$index]['occurrences'][$item] = new Carbon($occurrence); - } - } - - $subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]); - - return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups')); - } - } diff --git a/app/Http/Controllers/Recurring/ShowController.php b/app/Http/Controllers/Recurring/ShowController.php new file mode 100644 index 0000000000..bccf857f46 --- /dev/null +++ b/app/Http/Controllers/Recurring/ShowController.php @@ -0,0 +1,96 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Http\Controllers\Recurring; + + +use Carbon\Carbon; +use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Http\Controllers\Controller; +use FireflyIII\Models\Recurrence; +use FireflyIII\Repositories\Recurring\RecurringRepositoryInterface; +use FireflyIII\Support\Http\Controllers\GetConfigurationData; +use FireflyIII\Transformers\RecurrenceTransformer; +use Symfony\Component\HttpFoundation\ParameterBag; + +/** + * + * Class ShowController + */ +class ShowController extends Controller +{ + use GetConfigurationData; + /** @var RecurringRepositoryInterface Recurring repository */ + private $recurring; + + /** + * IndexController constructor. + * @codeCoverageIgnore + */ + public function __construct() + { + parent::__construct(); + + // translations: + $this->middleware( + function ($request, $next) { + app('view')->share('mainTitleIcon', 'fa-paint-brush'); + app('view')->share('title', (string)trans('firefly.recurrences')); + + $this->recurring = app(RecurringRepositoryInterface::class); + + return $next($request); + } + ); + } + + + /** + * Show a single recurring transaction. + * + * @param Recurrence $recurrence + * + * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View + * @throws FireflyException + */ + public function show(Recurrence $recurrence) + { + /** @var RecurrenceTransformer $transformer */ + $transformer = app(RecurrenceTransformer::class); + $transformer->setParameters(new ParameterBag); + + $array = $transformer->transform($recurrence); + $groups = $this->recurring->getTransactions($recurrence); + + // transform dates back to Carbon objects: + foreach ($array['repetitions'] as $index => $repetition) { + foreach ($repetition['occurrences'] as $item => $occurrence) { + $array['repetitions'][$index]['occurrences'][$item] = new Carbon($occurrence); + } + } + + $subTitle = (string)trans('firefly.overview_for_recurrence', ['title' => $recurrence->title]); + + return view('recurring.show', compact('recurrence', 'subTitle', 'array', 'groups')); + } +} \ No newline at end of file diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index ae903cf4e1..8893aaeee8 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -86,15 +86,11 @@ class RecurrenceFormRequest extends Request 'budget_name' => null, 'category_id' => null, 'category_name' => $this->string('category'), - + 'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [], + 'piggy_bank_id' => $this->integer('piggy_bank_id'), + 'piggy_bank_name' => null, ], ], - 'meta' => [ - // tags and piggy bank ID. - 'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [], - 'piggy_bank_id' => $this->integer('piggy_bank_id'), - 'piggy_bank_name' => null, - ], 'repetitions' => [ [ 'type' => $repetitionData['type'], @@ -137,86 +133,6 @@ class RecurrenceFormRequest extends Request return $return; } - - /** - * Configure the validator instance with special rules for after the basic validation rules. - * - * @param Validator $validator - * - * @return void - */ - public function withValidator(Validator $validator): void - { - $validator->after( - function (Validator $validator) { - // validate all account info - $this->validateAccountInformation($validator); - } - ); - } - - /** - * Validates the given account information. Switches on given transaction type. - * - * @param Validator $validator - * @throws FireflyException - */ - public function validateAccountInformation(Validator $validator): void - { - Log::debug('Now in validateAccountInformation()'); - /** @var AccountValidator $accountValidator */ - $accountValidator = app(AccountValidator::class); - $data = $validator->getData(); - $transactionType = $data['transaction_type'] ?? 'invalid'; - - $accountValidator->setTransactionType($transactionType); - - // default values: - $sourceId = null; - $destinationId = null; - - switch ($this->string('transaction_type')) { - default: - throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->string('transaction_type'))); // @codeCoverageIgnore - case 'withdrawal': - $sourceId = (int)$data['source_id']; - $destinationId = (int)$data['withdrawal_destination_id']; - break; - case 'deposit': - $sourceId = (int)$data['deposit_source_id']; - $destinationId = (int)$data['destination_id']; - break; - case 'transfer': - $sourceId = (int)$data['source_id']; - $destinationId = (int)$data['destination_id']; - break; - } - - - // validate source account. - $validSource = $accountValidator->validateSource($sourceId, null); - - // do something with result: - if (false === $validSource) { - $message = (string)trans('validation.generic_invalid_source'); - $validator->errors()->add('source_id', $message); - $validator->errors()->add('deposit_source_id', $message); - - return; - } - - // validate destination account - $validDestination = $accountValidator->validateDestination($destinationId, null); - // do something with result: - if (false === $validDestination) { - $message = (string)trans('validation.generic_invalid_destination'); - $validator->errors()->add('destination_id', $message); - $validator->errors()->add('withdrawal_destination_id', $message); - - return; - } - } - /** * The rules for this request. * @@ -310,6 +226,86 @@ class RecurrenceFormRequest extends Request return $rules; } + /** + * Validates the given account information. Switches on given transaction type. + * + * @param Validator $validator + * + * @throws FireflyException + */ + public function validateAccountInformation(Validator $validator): void + { + Log::debug('Now in validateAccountInformation()'); + /** @var AccountValidator $accountValidator */ + $accountValidator = app(AccountValidator::class); + $data = $validator->getData(); + $transactionType = $data['transaction_type'] ?? 'invalid'; + + $accountValidator->setTransactionType($transactionType); + + // default values: + $sourceId = null; + $destinationId = null; + + switch ($this->string('transaction_type')) { + default: + throw new FireflyException(sprintf('Cannot handle transaction type "%s"', $this->string('transaction_type'))); // @codeCoverageIgnore + case 'withdrawal': + $sourceId = (int)$data['source_id']; + $destinationId = (int)$data['withdrawal_destination_id']; + break; + case 'deposit': + $sourceId = (int)$data['deposit_source_id']; + $destinationId = (int)$data['destination_id']; + break; + case 'transfer': + $sourceId = (int)$data['source_id']; + $destinationId = (int)$data['destination_id']; + break; + } + + + // validate source account. + $validSource = $accountValidator->validateSource($sourceId, null); + + // do something with result: + if (false === $validSource) { + $message = (string)trans('validation.generic_invalid_source'); + $validator->errors()->add('source_id', $message); + $validator->errors()->add('deposit_source_id', $message); + + return; + } + + // validate destination account + $validDestination = $accountValidator->validateDestination($destinationId, null); + // do something with result: + if (false === $validDestination) { + $message = (string)trans('validation.generic_invalid_destination'); + $validator->errors()->add('destination_id', $message); + $validator->errors()->add('withdrawal_destination_id', $message); + + return; + } + } + + /** + * Configure the validator instance with special rules for after the basic validation rules. + * + * @param Validator $validator + * + * @return void + */ + public function withValidator(Validator $validator): void + { + $validator->after( + function (Validator $validator) { + // validate all account info + $this->validateAccountInformation($validator); + } + ); + } + /** * Parses repetition data. * diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php index 063d5272e2..14bbce26a2 100644 --- a/app/Services/Internal/Update/RecurrenceUpdateService.php +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -91,7 +91,7 @@ class RecurrenceUpdateService // update all meta data: //$this->updateMetaData($recurrence, $data); - if (null !== $data['recurrence']['notes']) { + if (isset($data['recurrence']['notes']) && null !== $data['recurrence']['notes']) { $this->setNoteText($recurrence, $data['recurrence']['notes']); } diff --git a/app/Transformers/RecurrenceTransformer.php b/app/Transformers/RecurrenceTransformer.php index d5dd6db637..2615418425 100644 --- a/app/Transformers/RecurrenceTransformer.php +++ b/app/Transformers/RecurrenceTransformer.php @@ -178,6 +178,8 @@ class RecurrenceTransformer extends AbstractTransformer switch ($transactionMeta->name) { default: throw new FireflyException(sprintf('Recurrence transformer cant handle field "%s"', $transactionMeta->name)); + case 'bill_id': + break; case 'tags': $array['tags'] = json_decode($transactionMeta->value); break; diff --git a/resources/lang/en_US/list.php b/resources/lang/en_US/list.php index d1556419fc..481ae46017 100644 --- a/resources/lang/en_US/list.php +++ b/resources/lang/en_US/list.php @@ -23,65 +23,66 @@ declare(strict_types=1); return [ - 'buttons' => 'Buttons', - 'icon' => 'Icon', - 'id' => 'ID', - 'create_date' => 'Created at', - 'update_date' => 'Updated at', - 'updated_at' => 'Updated at', - 'balance_before' => 'Balance before', - 'balance_after' => 'Balance after', - 'name' => 'Name', - 'role' => 'Role', - 'currentBalance' => 'Current balance', - 'linked_to_rules' => 'Relevant rules', - 'active' => 'Is active?', - 'transaction_type' => 'Type', - 'lastActivity' => 'Last activity', - 'balanceDiff' => 'Balance difference', - 'matchesOn' => 'Matched on', - 'account_type' => 'Account type', - 'created_at' => 'Created at', - 'account' => 'Account', - 'matchingAmount' => 'Amount', - 'split_number' => 'Split #', - 'destination' => 'Destination', - 'source' => 'Source', - 'next_expected_match' => 'Next expected match', - 'automatch' => 'Auto match?', - 'repeat_freq' => 'Repeats', - 'description' => 'Description', - 'amount' => 'Amount', - 'internal_reference' => 'Internal reference', - 'date' => 'Date', - 'interest_date' => 'Interest date', - 'book_date' => 'Book date', - 'process_date' => 'Processing date', - 'due_date' => 'Due date', - 'payment_date' => 'Payment date', - 'invoice_date' => 'Invoice date', - 'interal_reference' => 'Internal reference', - 'notes' => 'Notes', - 'from' => 'From', - 'piggy_bank' => 'Piggy bank', - 'to' => 'To', - 'budget' => 'Budget', - 'category' => 'Category', - 'bill' => 'Bill', - 'withdrawal' => 'Withdrawal', - 'deposit' => 'Deposit', - 'transfer' => 'Transfer', - 'type' => 'Type', - 'completed' => 'Completed', - 'iban' => 'IBAN', - 'paid_current_period' => 'Paid this period', - 'email' => 'Email', - 'registered_at' => 'Registered at', - 'is_blocked' => 'Is blocked', - 'is_admin' => 'Is admin', - 'has_two_factor' => 'Has 2FA', - 'blocked_code' => 'Block code', - 'source_account' => 'Source account', + 'buttons' => 'Buttons', + 'icon' => 'Icon', + 'id' => 'ID', + 'create_date' => 'Created at', + 'update_date' => 'Updated at', + 'updated_at' => 'Updated at', + 'balance_before' => 'Balance before', + 'balance_after' => 'Balance after', + 'name' => 'Name', + 'role' => 'Role', + 'currentBalance' => 'Current balance', + 'linked_to_rules' => 'Relevant rules', + 'active' => 'Is active?', + 'transaction_type' => 'Type', + 'lastActivity' => 'Last activity', + 'balanceDiff' => 'Balance difference', + 'matchesOn' => 'Matched on', + 'other_meta_data' => 'Other meta data', + 'account_type' => 'Account type', + 'created_at' => 'Created at', + 'account' => 'Account', + 'matchingAmount' => 'Amount', + 'split_number' => 'Split #', + 'destination' => 'Destination', + 'source' => 'Source', + 'next_expected_match' => 'Next expected match', + 'automatch' => 'Auto match?', + 'repeat_freq' => 'Repeats', + 'description' => 'Description', + 'amount' => 'Amount', + 'internal_reference' => 'Internal reference', + 'date' => 'Date', + 'interest_date' => 'Interest date', + 'book_date' => 'Book date', + 'process_date' => 'Processing date', + 'due_date' => 'Due date', + 'payment_date' => 'Payment date', + 'invoice_date' => 'Invoice date', + 'interal_reference' => 'Internal reference', + 'notes' => 'Notes', + 'from' => 'From', + 'piggy_bank' => 'Piggy bank', + 'to' => 'To', + 'budget' => 'Budget', + 'category' => 'Category', + 'bill' => 'Bill', + 'withdrawal' => 'Withdrawal', + 'deposit' => 'Deposit', + 'transfer' => 'Transfer', + 'type' => 'Type', + 'completed' => 'Completed', + 'iban' => 'IBAN', + 'paid_current_period' => 'Paid this period', + 'email' => 'Email', + 'registered_at' => 'Registered at', + 'is_blocked' => 'Is blocked', + 'is_admin' => 'Is admin', + 'has_two_factor' => 'Has 2FA', + 'blocked_code' => 'Block code', + 'source_account' => 'Source account', 'destination_account' => 'Destination account', 'accounts_count' => 'Number of accounts', 'journals_count' => 'Number of transactions', diff --git a/resources/views/v1/recurring/edit.twig b/resources/views/v1/recurring/edit.twig index 7fdb36ba2d..0b466d4d8b 100644 --- a/resources/views/v1/recurring/edit.twig +++ b/resources/views/v1/recurring/edit.twig @@ -125,20 +125,10 @@ {{ ExpandedForm.text('category',array.transactions[0].category_name) }} {# TAGS #} - {% set tags = '' %} - {% set piggyBankId = 0 %} - {% for metaValue in array.meta %} - {% if metaValue.name == 'tags' %} - {% set tags = metaValue.value %} - {% endif %} - {% if metaValue.name == 'piggy_bank_id' %} - {% set piggyBankId = metaValue.value %} - {% endif %} - {% endfor %} - {{ ExpandedForm.text('tags', tags) }} + {{ ExpandedForm.text('tags', array.transactions[0].tags) }} {# RELATE THIS TRANSFER TO A PIGGY BANK #} - {{ PiggyBankForm.piggyBankList('piggy_bank_id',piggyBankId) }} + {{ PiggyBankForm.piggyBankList('piggy_bank_id',array.transactions[0].piggy_bank_id) }} diff --git a/resources/views/v1/recurring/show.twig b/resources/views/v1/recurring/show.twig index 845e5dc649..b60f10377d 100644 --- a/resources/views/v1/recurring/show.twig +++ b/resources/views/v1/recurring/show.twig @@ -5,188 +5,153 @@ {% endblock %} {% block content %} -
- -
-
-
-

- {{ array.title }} +
+ +
+
+
+

+ {{ array.title }} - ({{ array.transaction_type }}) - - {% if array.active == false %} - ({{ 'inactive'|_|lower }}) - {% endif %} -

-
-
-

{{ array.description }}

+ ({{ array.type }}) {% if array.active == false %} -

- {{ 'recurrence_is_inactive'|_ }} -

+ ({{ 'inactive'|_|lower }}) {% endif %} - -
    - {% for rep in array.repetitions %} -
  • {{ rep.description }}
  • - {% endfor %} -
-
- +

-
- -
-
-
-

- {{ ('expected_'~array.transaction_type~'s')|_ }} -

-
-
+
+

{{ array.description }}

-
    - {% for rep in array.recurrence_repetitions %} -
  • - {{ rep.description }} - {% if rep.repetition_skip == 1 %} - ({{ trans('firefly.recurring_skips_one')|lower }}) - {% endif %} - {% if rep.repetition_skip > 1 %} - ({{ trans('firefly.recurring_skips_more', {count: rep.repetition_skip})|lower }}) - {% endif %} -
      - {% for occ in rep.occurrences %} -
    • {{ occ.formatLocalized(trans('config.month_and_date_day')) }}
    • - {% endfor %} -
    -
  • - {% endfor %} -
-
- +
-
- -
-
-
-

- {{ 'transaction_data'|_ }} -

-
-
- - + +
+
+
+

+ {{ ('expected_'~array.type~'s')|_ }} +

+
+
+ +
    + {% for rep in array.repetitions %} +
  • + {{ rep.description }} + {% if rep.repetition_skip == 1 %} + ({{ trans('firefly.recurring_skips_one')|lower }}) + {% endif %} + {% if rep.repetition_skip > 1 %} + ({{ trans('firefly.recurring_skips_more', {count: rep.repetition_skip})|lower }}) + {% endif %} +
      + {% for occ in rep.occurrences %} +
    • {{ occ.formatLocalized(trans('config.month_and_date_day')) }}
    • + {% endfor %} +
    +
  • + {% endfor %} +
+
+ +
+
+ +
+ +
+
+
+

+ {{ 'transaction_data'|_ }} +

+
+
+
+ + - - - {% for transaction in array.transactions %} - - - - - - - - - {% endfor %} - -
{{ trans('list.description') }} {{ trans('list.source') }} {{ trans('list.destination') }} {{ trans('list.amount') }} {{ trans('list.category') }} {{ trans('list.budget') }}
- {{ transaction.description }} - - {{ transaction.source_name }} - - {{ transaction.destination_name }} - - {{ formatAmountBySymbol(transaction.amount,transaction.currency_symbol,transaction.currency_decimal_places) }} - {% if null != transaction.foreign_amount %} - ({{ formatAmountBySymbol(transaction.foreign_amount,transaction.foreign_currency_symbol,transaction.foreign_currency_decimal_places) }}) - {% endif %} - - {% for meta in transaction.meta %} - {% if meta.name == 'category_name' %} - - {{ meta.category_name }} - - {% endif %} - {% endfor %} - - {% for meta in transaction.meta %} - {% if meta.name == 'budget_id' %} - - {{ meta.budget_name }} - - {% endif %} - {% endfor %} -
-
+ {{ trans('list.other_meta_data') }} + + + + {% for transaction in array.transactions %} + + + {{ transaction.description }} + + + {{ transaction.source_name }} + + + {{ transaction.destination_name }} + + + {{ formatAmountBySymbol(transaction.amount,transaction.currency_symbol,transaction.currency_decimal_places) }} + {% if null != transaction.foreign_amount %} + ({{ formatAmountBySymbol(transaction.foreign_amount,transaction.foreign_currency_symbol,transaction.foreign_currency_decimal_places) }}) + {% endif %} + + + {% if '' != transaction.category_name %} + + {{ transaction.category_name }} + + {% endif %} + + + {% if '' != transaction.budget_name %} + + {{ transaction.budget_name }} + + {% endif %} + + + {% if transaction.tags|length > 0 %} +

+ {% for tag in transaction.tags %} + {{ tag }} + {% endfor %} +

+ {% endif %} + {% if 0 != transaction.piggy_bank_id and array.type == 'Transfer' %} + {{ transaction.piggy_bank_name }} + {% endif %} + + + {% endfor %} + +
- - - {% if array.meta|length > 0 %} -
-
-
-

- {{ 'meta_data'|_ }} -

-
-
- - - - - - - {% for meta in array.meta %} - - - - - {% endfor %} - - -
{{ trans('list.field') }}{{ trans('list.value') }}
{{ trans('firefly.recurring_meta_field_'~meta.name) }} - {% if meta.name == 'tags' %} - {% for tag in meta.tags %} - {{ tag }} - {% endfor %} - {% endif %} - {% if meta.name == 'notes' %} - {{ meta.value|markdown }} - {% endif %} - {% if meta.name == 'bill_id' %} - {{ meta.bill_name }} - {% endif %} - {% if meta.name == 'piggy_bank_id' %} - {{ meta.piggy_bank_name }} - {% endif %} -
-
-
-
- {% endif %}
@@ -203,14 +168,14 @@
-{% endblock %} + {% endblock %} -{% block styles %} - -{% endblock %} + {% block styles %} + + {% endblock %} -{% block scripts %} - - {# required for groups.twig #} - -{% endblock %} + {% block scripts %} + + {# required for groups.twig #} + + {% endblock %} diff --git a/routes/web.php b/routes/web.php index a3db7a4986..61efe0ceae 100644 --- a/routes/web.php +++ b/routes/web.php @@ -657,7 +657,7 @@ Route::group( Route::get('', ['uses' => 'Recurring\IndexController@index', 'as' => 'index']); - Route::get('show/{recurrence}', ['uses' => 'Recurring\IndexController@show', 'as' => 'show']); + Route::get('show/{recurrence}', ['uses' => 'Recurring\ShowController@show', 'as' => 'show']); Route::get('create', ['uses' => 'Recurring\CreateController@create', 'as' => 'create']); Route::get('edit/{recurrence}', ['uses' => 'Recurring\EditController@edit', 'as' => 'edit']); Route::get('delete/{recurrence}', ['uses' => 'Recurring\DeleteController@delete', 'as' => 'delete']);