diff --git a/app/Factory/RecurrenceFactory.php b/app/Factory/RecurrenceFactory.php index 4ce667d307..4de18bfd91 100644 --- a/app/Factory/RecurrenceFactory.php +++ b/app/Factory/RecurrenceFactory.php @@ -80,6 +80,7 @@ class RecurrenceFactory $description = ''; $applyRules = true; $active = true; + $repeatUntilString = null; if (array_key_exists('first_date', $data['recurrence'])) { /** @var Carbon $firstDate */ $firstDate = $data['recurrence']['first_date']; @@ -102,8 +103,8 @@ class RecurrenceFactory if (array_key_exists('active', $data['recurrence'])) { $active = $data['recurrence']['active']; } - if ($repetitions > 0 && null === $repeatUntil) { - $repeatUntil = Carbon::create()->addyear(); + if (null !== $repeatUntil) { + $repeatUntilString = $repeatUntil->format('Y-m-d'); } $recurrence = new Recurrence( @@ -113,7 +114,7 @@ class RecurrenceFactory 'title' => $title, 'description' => $description, 'first_date' => $firstDate ? $firstDate->format('Y-m-d') : null, - 'repeat_until' => $repetitions > 0 ? null : $repeatUntil->format('Y-m-d'), + 'repeat_until' => $repetitions > 0 ? null : $repeatUntilString, 'latest_date' => null, 'repetitions' => $repetitions, 'apply_rules' => $applyRules, diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index 7217cc527f..2f9a3faadd 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\Factory\CategoryFactory; use FireflyIII\Models\Recurrence; use FireflyIII\Models\TransactionType; use FireflyIII\Rules\ValidRecurrenceRepetitionType; @@ -123,6 +124,19 @@ class RecurrenceFormRequest extends FormRequest break; } + // replace category name with a new category: + $factory = app(CategoryFactory::class); + $factory->setUser(auth()->user()); + foreach($return['transactions'] as $index => $transaction) { + $categoryName =$transaction['category_name'] ??null; + if(null !== $categoryName) { + $category = $factory->findOrCreate(null, $categoryName); + if(null !== $category) { + $return['transactions'][$index]['category_id'] = $category->id; + } + } + } + return $return; } diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index e58312b75b..41b03e5407 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -138,7 +138,9 @@ trait RecurringTransactionTrait if (!$validator->validateDestination($destination->id, null, null)) { throw new FireflyException(sprintf('Destination invalid: %s', $validator->destError)); // @codeCoverageIgnore } - + if(array_key_exists('foreign_amount', $array) && '' === (string)$array['foreign_amount']) { + unset($array['foreign_amount']); + } // TODO typeOverrule: the account validator may have another opinion on the transaction type. $transaction = new RecurrenceTransaction( [ diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php index 71332053f1..f4bacda0a6 100644 --- a/app/Services/Internal/Update/RecurrenceUpdateService.php +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -28,6 +28,7 @@ use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Note; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceRepetition; +use FireflyIII\Models\RecurrenceTransaction; use FireflyIII\Services\Internal\Support\RecurringTransactionTrait; use FireflyIII\Services\Internal\Support\TransactionTypeTrait; use FireflyIII\User; @@ -103,15 +104,73 @@ class RecurrenceUpdateService // update all transactions: -// // update all transactions (and associated meta-data) -// if (array_key_exists('transactions', $data)) { -// $this->deleteTransactions($recurrence); -// $this->createTransactions($recurrence, $data['transactions'] ?? []); -// } + // update all transactions (and associated meta-data) + if (array_key_exists('transactions', $data)) { + $this->updateTransactions($recurrence, $data['transactions'] ?? []); + // $this->deleteTransactions($recurrence); + // $this->createTransactions($recurrence, $data['transactions'] ?? []); + } return $recurrence; } + /** + * TODO this method is way too complex. + * + * @param Recurrence $recurrence + * @param array $transactions + * + * @throws FireflyException + */ + private function updateTransactions(Recurrence $recurrence, array $transactions): void + { + $originalCount = $recurrence->recurrenceTransactions()->count(); + if (0 === count($transactions)) { + // wont drop transactions, rather avoid. + return; + } + // user added or removed repetitions, delete all and recreate: + if ($originalCount !== count($transactions)) { + Log::debug('Del + recreate'); + $this->deleteTransactions($recurrence); + $this->createTransactions($recurrence, $transactions); + + return; + } + // loop all and try to match them: + if ($originalCount === count($transactions)) { + Log::debug('Loop and find'); + foreach ($transactions as $current) { + $match = $this->matchTransaction($recurrence, $current); + if (null === $match) { + throw new FireflyException('Cannot match recurring transaction to existing transaction. Not sure what to do. Break.'); + } + // TODO find currency + // TODO find foreign currency + + // update fields + $fields = [ + 'source_id' => 'source_id', + 'destination_id' => 'destination_id', + 'amount' => 'amount', + 'foreign_amount' => 'foreign_amount', + 'description' => 'description', + ]; + foreach ($fields as $field => $column) { + if (array_key_exists($field, $current)) { + $match->$column = $current[$field]; + $match->save(); + } + } + // update meta data + // budget_id + // category_id + // tags + // piggy_bank_id + } + } + } + /** * @param Recurrence $recurrence * @param string $text @@ -172,7 +231,7 @@ class RecurrenceUpdateService 'moment' => 'repetition_moment', 'skip' => 'repetition_skip', 'weekend' => 'weekend', - ]; + ]; foreach ($fields as $field => $column) { if (array_key_exists($field, $current)) { $match->$column = $current[$field]; @@ -193,6 +252,7 @@ class RecurrenceUpdateService $originalCount = $recurrence->recurrenceRepetitions()->count(); if (1 === $originalCount) { Log::debug('Return the first one'); + return $recurrence->recurrenceRepetitions()->first(); } // find it: @@ -211,4 +271,38 @@ class RecurrenceUpdateService return $query->first(); } + + /** + * @param array $data + * + * @return RecurrenceTransaction|null + */ + private function matchTransaction(Recurrence $recurrence, array $data): ?RecurrenceTransaction + { + $originalCount = $recurrence->recurrenceTransactions()->count(); + if (1 === $originalCount) { + Log::debug('Return the first one'); + + return $recurrence->recurrenceTransactions()->first(); + } + // find it based on data + $fields = [ + 'id' => 'id', + 'currency_id' => 'transaction_currency_id', + 'foreign_currency_id' => 'foreign_currency_id', + 'source_id' => 'source_id', + 'destination_id' => 'destination_id', + 'amount' => 'amount', + 'foreign_amount' => 'foreign_amount', + 'description' => 'description', + ]; + $query = $recurrence->recurrenceTransactions(); + foreach ($fields as $field => $column) { + if (array_key_exists($field, $data)) { + $query->where($column, $data[$field]); + } + } + + return $query->first(); + } } diff --git a/tests/Api/Models/Recurrence/UpdateControllerTest.php b/tests/Api/Models/Recurrence/UpdateControllerTest.php index b7070778ba..70a5cc7f5d 100644 --- a/tests/Api/Models/Recurrence/UpdateControllerTest.php +++ b/tests/Api/Models/Recurrence/UpdateControllerTest.php @@ -212,29 +212,26 @@ class UpdateControllerTest extends TestCase // now loop fields, enough to create sets I guess? // TODO maybe do some permutation stuff here? $extraTransaction = [ - 'description' => $faker->uuid, - 'amount' => number_format($faker->randomFloat(2, 10, 100), 2), - 'skip' => $faker->numberBetween(1, 3), - 'weekend' => $faker->numberBetween(1, 4), - 'budget_id' => $faker->numberBetween(1, 2), - 'category_id' => $faker->numberBetween(1, 2), - 'tags' => ['a', 'c', 'd'], - 'source_id' => 1, - 'destination_id' => 8, + 'currency_id' => $faker->numberBetween(1, 4), + 'foreign_currency_id' => $faker->numberBetween(4, 6), + 'source_id' => $faker->numberBetween(1, 3), + 'destination_id' => $faker->numberBetween(8), + 'amount' => number_format($faker->randomFloat(2, 10, 100), 2), + 'foreign_amount' => number_format($faker->randomFloat(2, 10, 100), 2), + 'description' => $faker->uuid, ]; $extraTransactions[] = $extraTransaction; } - // TODO later maybe -// $set[] = [ -// 'id' => 1, -// 'fields' => [ -// 'transactions' => [ -// 'test_value' => $extraTransactions, -// ], -// ], -// 'extra_ignore' => [], -// ]; + $set[] = [ + 'id' => 1, + 'fields' => [ + 'transactions' => [ + 'test_value' => $extraTransactions, + ], + ], + 'extra_ignore' => [], + ]; } return $set;