diff --git a/app/Api/V1/Controllers/RecurrenceController.php b/app/Api/V1/Controllers/RecurrenceController.php index 84af2a235a..dce57a0d98 100644 --- a/app/Api/V1/Controllers/RecurrenceController.php +++ b/app/Api/V1/Controllers/RecurrenceController.php @@ -155,14 +155,25 @@ class RecurrenceController extends Controller } /** - * @param Request $request - * @param string $object + * @param RecurrenceRequest $request + * @param Recurrence $recurrence * * @return JsonResponse */ - public function update(Request $request, string $object): JsonResponse + public function update(RecurrenceRequest $request, Recurrence $recurrence): JsonResponse { - // todo replace code and replace request object. + $data = $request->getAll(); + + // + + $category = $this->repository->update($recurrence, $data); + $manager = new Manager(); + $baseUrl = $request->getSchemeAndHttpHost() . '/api/v1'; + $manager->setSerializer(new JsonApiSerializer($baseUrl)); + + $resource = new Item($category, new RecurrenceTransformer($this->parameters), 'recurrences'); + + return response()->json($manager->createData($resource)->toArray())->header('Content-Type', 'application/vnd.api+json'); } } \ No newline at end of file diff --git a/app/Api/V1/Requests/RecurrenceRequest.php b/app/Api/V1/Requests/RecurrenceRequest.php index 3dce86b02f..c7c3c04951 100644 --- a/app/Api/V1/Requests/RecurrenceRequest.php +++ b/app/Api/V1/Requests/RecurrenceRequest.php @@ -124,7 +124,7 @@ class RecurrenceRequest extends Request return [ 'type' => 'required|in:withdrawal,transfer,deposit', - 'title' => 'required|between:1,255', + 'title' => 'required|between:1,255|uniqueObjectForUser:recurrences,title', 'description' => 'between:1,65000', 'first_date' => sprintf('required|date|after:%s', $today->format('Y-m-d')), 'repeat_until' => sprintf('date|after:%s', $today->format('Y-m-d')), @@ -139,8 +139,8 @@ class RecurrenceRequest extends Request // rules for repetitions. 'repetitions.*.type' => 'required|in:daily,weekly,ndom,monthly,yearly', 'repetitions.*.moment' => 'between:0,10', - 'repetitions.*.skip' => 'required|between:0,31', - 'repetitions.*.weekend' => 'required|between:1,4', + 'repetitions.*.skip' => 'required|numeric|between:0,31', + 'repetitions.*.weekend' => 'required|numeric|min:1|max:4', // rules for transactions. 'transactions.*.currency_id' => 'numeric|exists:transaction_currencies,id|required_without:transactions.*.currency_code', diff --git a/app/Factory/CategoryFactory.php b/app/Factory/CategoryFactory.php index ff80a61a7b..245d8e9eb6 100644 --- a/app/Factory/CategoryFactory.php +++ b/app/Factory/CategoryFactory.php @@ -72,7 +72,7 @@ class CategoryFactory Log::debug(sprintf('Going to find category with ID %d and name "%s"', $categoryId, $categoryName)); - if (\strlen($categoryName) === 0 && $categoryId === 0) { + if ('' === $categoryName && $categoryId === 0) { return null; } // first by ID: diff --git a/app/Http/Requests/RecurrenceFormRequest.php b/app/Http/Requests/RecurrenceFormRequest.php index ad18aa3cf1..7bebfacbce 100644 --- a/app/Http/Requests/RecurrenceFormRequest.php +++ b/app/Http/Requests/RecurrenceFormRequest.php @@ -66,21 +66,26 @@ class RecurrenceFormRequest extends Request ], 'transactions' => [ [ - 'transaction_currency_id' => $this->integer('transaction_currency_id'), - 'type' => $this->string('transaction_type'), - 'description' => $this->string('transaction_description'), - 'amount' => $this->string('amount'), - 'foreign_amount' => null, - 'foreign_currency_id' => null, - 'budget_id' => $this->integer('budget_id'), - 'category_name' => $this->string('category'), + 'currency_id' => $this->integer('transaction_currency_id'), + 'currency_code' => null, + 'type' => $this->string('transaction_type'), + 'description' => $this->string('transaction_description'), + 'amount' => $this->string('amount'), + 'foreign_amount' => null, + 'foreign_currency_id' => null, + 'foreign_currency_code' => null, + 'budget_id' => $this->integer('budget_id'), + 'budget_name' => null, + 'category_id' => null, + 'category_name' => $this->string('category'), ], ], 'meta' => [ // tags and piggy bank ID. - 'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [], - 'piggy_bank_id' => $this->integer('piggy_bank_id'), + 'tags' => '' !== $this->string('tags') ? explode(',', $this->string('tags')) : [], + 'piggy_bank_id' => $this->integer('piggy_bank_id'), + 'piggy_bank_name' => null, ], 'repetitions' => [ [ @@ -98,7 +103,11 @@ class RecurrenceFormRequest extends Request $return['transactions'][0]['foreign_amount'] = $this->string('foreign_amount'); $return['transactions'][0]['foreign_currency_id'] = $this->integer('foreign_currency_id'); } - + // default values: + $return['transactions'][0]['source_id'] = null; + $return['transactions'][0]['source_name'] = null; + $return['transactions'][0]['destination_id'] = null; + $return['transactions'][0]['destination_name'] = null; // fill in source and destination account data switch ($this->string('transaction_type')) { default: diff --git a/app/Models/RecurrenceTransaction.php b/app/Models/RecurrenceTransaction.php index 8687733929..bea0b38ff6 100644 --- a/app/Models/RecurrenceTransaction.php +++ b/app/Models/RecurrenceTransaction.php @@ -73,7 +73,7 @@ class RecurrenceTransaction extends Model */ public function destinationAccount(): BelongsTo { - return $this->belongsTo(Account::class); + return $this->belongsTo(Account::class,'destination_id'); } /** @@ -109,7 +109,7 @@ class RecurrenceTransaction extends Model */ public function sourceAccount(): BelongsTo { - return $this->belongsTo(Account::class); + return $this->belongsTo(Account::class,'source_id'); } /** diff --git a/app/Models/TransactionJournal.php b/app/Models/TransactionJournal.php index 760f4831da..7ec25a1266 100644 --- a/app/Models/TransactionJournal.php +++ b/app/Models/TransactionJournal.php @@ -32,6 +32,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\SoftDeletes; +use Illuminate\Support\Collection; use Log; use Preferences; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; @@ -42,6 +43,7 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; * @property User $user * @property int $bill_id * @property Collection $categories + * @property bool $completed */ class TransactionJournal extends Model { diff --git a/app/Services/Internal/Support/RecurringTransactionTrait.php b/app/Services/Internal/Support/RecurringTransactionTrait.php index c57dce1f82..b36d7d4c0d 100644 --- a/app/Services/Internal/Support/RecurringTransactionTrait.php +++ b/app/Services/Internal/Support/RecurringTransactionTrait.php @@ -24,7 +24,10 @@ declare(strict_types=1); namespace FireflyIII\Services\Internal\Support; use Exception; -use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Factory\BudgetFactory; +use FireflyIII\Factory\CategoryFactory; +use FireflyIII\Factory\PiggyBankFactory; +use FireflyIII\Factory\TransactionCurrencyFactory; use FireflyIII\Models\AccountType; use FireflyIII\Models\Recurrence; use FireflyIII\Models\RecurrenceMeta; @@ -69,32 +72,39 @@ trait RecurringTransactionTrait */ public function createTransactions(Recurrence $recurrence, array $transactions): void { - foreach ($transactions as $array) { $source = null; $destination = null; switch ($recurrence->transactionType->type) { case TransactionType::WITHDRAWAL: - $source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name'] ?? null); - $destination = $this->findAccount(AccountType::EXPENSE, $array['destination_id'] ?? null, $array['destination_name']); + $source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name']); + $destination = $this->findAccount(AccountType::EXPENSE, $array['destination_id'], $array['destination_name']); break; case TransactionType::DEPOSIT: - $source = $this->findAccount(AccountType::REVENUE, $array['source_id'] ?? null, $array['source_name']); - $destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name'] ?? null); + $source = $this->findAccount(AccountType::REVENUE, $array['source_id'], $array['source_name']); + $destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name']); break; case TransactionType::TRANSFER: - $source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name'] ?? null); - $destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name'] ?? null); + $source = $this->findAccount(AccountType::ASSET, $array['source_id'], $array['source_name']); + $destination = $this->findAccount(AccountType::ASSET, $array['destination_id'], $array['destination_name']); break; } + /** @var TransactionCurrencyFactory $factory */ + $factory = app(TransactionCurrencyFactory::class); + $currency = $factory->find($array['currency_id'], $array['currency_code']); + $foreignCurrency = $factory->find($array['foreign_currency_id'], $array['foreign_currency_code']); + $defaultCurrency = app('amount')->getDefaultCurrencyByUser($recurrence->user); + if (null === $currency) { + $currency = $defaultCurrency; + } $transaction = new RecurrenceTransaction( [ 'recurrence_id' => $recurrence->id, - 'transaction_currency_id' => $array['transaction_currency_id'], - 'foreign_currency_id' => '' === (string)$array['foreign_currency_id'] ? null : $array['foreign_currency_id'], - 'source_id' => $source->id, - 'destination_id' => $destination->id, + 'transaction_currency_id' => $currency->id, + 'foreign_currency_id' => null === $foreignCurrency ? null : $foreignCurrency->id, + 'source_id' => $source->id, + 'destination_id' => $destination->id, 'amount' => $array['amount'], 'foreign_amount' => '' === (string)$array['foreign_amount'] ? null : (string)$array['foreign_amount'], 'description' => $array['description'], @@ -102,22 +112,32 @@ trait RecurringTransactionTrait ); $transaction->save(); + /** @var BudgetFactory $budgetFactory */ + $budgetFactory = app(BudgetFactory::class); + $budgetFactory->setUser($recurrence->user); + $budget = $budgetFactory->find($array['budget_id'], $array['budget_name']); + + /** @var CategoryFactory $categoryFactory */ + $categoryFactory = app(CategoryFactory::class); + $categoryFactory->setUser($recurrence->user); + $category = $categoryFactory->findOrCreate($array['category_id'], $array['category_name']); + // create recurrence transaction meta: - if ($array['budget_id'] > 0) { + if (null !== $budget) { RecurrenceTransactionMeta::create( [ 'rt_id' => $transaction->id, 'name' => 'budget_id', - 'value' => $array['budget_id'], + 'value' => $budget->id, ] ); } - if ('' !== (string)$array['category_name']) { + if (null !== $category) { RecurrenceTransactionMeta::create( [ 'rt_id' => $transaction->id, 'name' => 'category_name', - 'value' => $array['category_name'], + 'value' => $category->name, ] ); } @@ -155,20 +175,27 @@ trait RecurringTransactionTrait 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) { + $piggyId = (int)($data['meta']['piggy_bank_id'] ?? 0.0); + $piggyName = $data['meta']['piggy_bank_name'] ?? ''; + /** @var PiggyBankFactory $factory */ + $factory = app(PiggyBankFactory::class); + $factory->setUser($recurrence->user); + $piggyBank = $factory->find($piggyId, $piggyName); + if (null !== $piggyBank) { /** @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 = RecurrenceMeta::create(['recurrence_id' => $recurrence->id, 'name' => 'piggy_bank_id', 'value' => $piggyBank->id]); } - $entry->value = $piggyId; + $entry->value = $piggyBank->id; $entry->save(); } - if ($piggyId === 0) { + if (null === $piggyBank) { // delete if present $recurrence->recurrenceMeta()->where('name', 'piggy_bank_id')->delete(); } + + $tags = $data['meta']['tags'] ?? []; if (\count($tags) > 0) { /** @var RecurrenceMeta $entry */ diff --git a/app/Services/Internal/Update/RecurrenceUpdateService.php b/app/Services/Internal/Update/RecurrenceUpdateService.php index 0c21caa630..241a36cf50 100644 --- a/app/Services/Internal/Update/RecurrenceUpdateService.php +++ b/app/Services/Internal/Update/RecurrenceUpdateService.php @@ -65,11 +65,13 @@ class RecurrenceUpdateService $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; + if(isset($data['recurrence']['repetition_end'])) { + 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();