From 77aced6734e1b46824b3c0e46fabf36742064e99 Mon Sep 17 00:00:00 2001 From: James Cole Date: Sun, 18 Feb 2018 16:35:26 +0100 Subject: [PATCH] Test every happy path for journal creation. --- app/Api/V1/Requests/TransactionRequest.php | 9 +- app/Factory/TagFactory.php | 65 ++- app/Factory/TransactionFactory.php | 142 ++++-- app/Factory/TransactionJournalFactory.php | 101 +++- app/Factory/TransactionJournalMetaFactory.php | 58 +++ app/Models/Transaction.php | 2 +- app/Transformers/TransactionTransformer.php | 9 + app/Validation/FireflyValidator.php | 4 +- .../Controllers/TransactionControllerTest.php | 445 +++++++++++++++++- 9 files changed, 768 insertions(+), 67 deletions(-) create mode 100644 app/Factory/TransactionJournalMetaFactory.php diff --git a/app/Api/V1/Requests/TransactionRequest.php b/app/Api/V1/Requests/TransactionRequest.php index 588aacacd0..69cbacf63e 100644 --- a/app/Api/V1/Requests/TransactionRequest.php +++ b/app/Api/V1/Requests/TransactionRequest.php @@ -32,6 +32,7 @@ use Illuminate\Validation\Validator; /** + * todo cannot submit using currency not part of source / dest * Class TransactionRequest */ class TransactionRequest extends Request @@ -75,7 +76,7 @@ class TransactionRequest extends Request 'transactions' => [], ]; - foreach ($this->get('transactions') as $transaction) { + foreach ($this->get('transactions') as $index => $transaction) { $array = [ 'description' => $transaction['description'] ?? null, 'amount' => $transaction['amount'], @@ -92,11 +93,12 @@ class TransactionRequest extends Request 'source_name' => $transaction['source_name'] ?? null, 'destination_id' => isset($transaction['destination_id']) ? intval($transaction['destination_id']) : null, 'destination_name' => $transaction['destination_name'] ?? null, - 'reconciled' => intval($transaction['reconciled'] ?? 0) === 1 ? true : false, - 'identifier' => isset($transaction['identifier']) ? intval($transaction['identifier']) : 0, + 'reconciled' => $transaction['reconciled'] ?? false, + 'identifier' => $index, ]; $data['transactions'][] = $array; } + return $data; } @@ -139,7 +141,6 @@ class TransactionRequest extends Request 'transactions.*.category_id' => ['mustExist:categories,id', new BelongsUser], 'transactions.*.category_name' => 'between:1,255|nullable', 'transactions.*.reconciled' => 'boolean|nullable', - 'transactions.*.identifier' => 'numeric|nullable', // basic rules will be expanded later. 'transactions.*.source_id' => ['numeric', 'nullable', new BelongsUser], 'transactions.*.source_name' => 'between:1,255|nullable', diff --git a/app/Factory/TagFactory.php b/app/Factory/TagFactory.php index 4cfe38e9b7..e1b9adbb62 100644 --- a/app/Factory/TagFactory.php +++ b/app/Factory/TagFactory.php @@ -25,12 +25,15 @@ namespace FireflyIII\Factory; use FireflyIII\Models\Tag; use FireflyIII\User; +use Illuminate\Support\Collection; /** * Class TagFactory */ class TagFactory { + /** @var Collection */ + private $tags; /** @var User */ private $user; @@ -42,14 +45,6 @@ class TagFactory } - /** - * @param User $user - */ - public function setUser(User $user): void - { - $this->user = $user; - } - /** * @param array $data * @@ -59,14 +54,56 @@ class TagFactory { return Tag::create( [ - 'user_id' => $data['user']->id, - 'tag' => $data['tag'], - 'tagMode' => 'nothing', - 'date' => $data['date'], - 'description'=> $data['description'], - + 'user_id' => $this->user->id, + 'tag' => $data['tag'], + 'tagMode' => 'nothing', + 'date' => $data['date'], + 'description' => $data['description'], + 'latitude' => $data['latitude'], + 'longitude ' => $data['longitude'], + 'zoomLevel' => $data['zoom_level'], ] ); } + /** + * @param string $tag + * + * @return Tag|null + */ + public function findOrCreate(string $tag): ?Tag + { + if (is_null($this->tags)) { + $this->tags = $this->user->tags()->get(); + } + + /** @var Tag $object */ + foreach ($this->tags as $object) { + if ($object->tag === $tag) { + return $object; + } + } + $newTag = $this->create( + [ + 'tag' => $tag, + 'date' => null, + 'description' => null, + 'latitude' => null, + 'longitude' => null, + 'zoom_level' => null, + ] + ); + $this->tags->push($newTag); + + return $newTag; + } + + /** + * @param User $user + */ + public function setUser(User $user): void + { + $this->user = $user; + } + } \ No newline at end of file diff --git a/app/Factory/TransactionFactory.php b/app/Factory/TransactionFactory.php index 3ee5723df9..154c733c5b 100644 --- a/app/Factory/TransactionFactory.php +++ b/app/Factory/TransactionFactory.php @@ -78,8 +78,7 @@ class TransactionFactory */ public function create(array $data): Transaction { - $foreignCurrencyId = is_null($data['foreign_currency']) ? null : $data['foreign_currency']->id; - $values = [ + $values = [ 'reconciled' => $data['reconciled'], 'account_id' => $data['account']->id, 'transaction_journal_id' => $data['transaction_journal']->id, @@ -87,14 +86,10 @@ class TransactionFactory 'transaction_currency_id' => $data['currency']->id, 'amount' => $data['amount'], 'foreign_amount' => $data['foreign_amount'], - 'foreign_currency_id' => $foreignCurrencyId, + 'foreign_currency_id' => null, 'identifier' => $data['identifier'], ]; - $transaction = $this->repository->storeBasicTransaction($values); - - // todo: add budget, category, etc. - // todo link budget, category - + $transaction = $this->repository->storeBasicTransaction($values); return $transaction; } @@ -110,8 +105,8 @@ class TransactionFactory public function createPair(TransactionJournal $journal, array $data): Collection { // all this data is the same for both transactions: - $currency = $this->findCurrency($data['currency_id'], $data['currency_code']); - $description = $journal->description === $data['description'] ? null : $data['description']; + $currency = $this->findCurrency($data['currency_id'], $data['currency_code']); + $description = $journal->description === $data['description'] ? null : $data['description']; // type of source account depends on journal type: $sourceType = $this->accountType($journal, 'source'); @@ -120,38 +115,52 @@ class TransactionFactory // same for destination account: $destinationType = $this->accountType($journal, 'destination'); $destinationAccount = $this->findAccount($destinationType, $data['destination_id'], $data['destination_name']); - // first make a "negative" (source) transaction based on the data in the array. - $sourceTransactionData = [ - 'description' => $description, - 'amount' => app('steam')->negative(strval($data['amount'])), - 'foreign_amount' => null, - 'currency' => $currency, - 'foreign_currency' => null, - 'budget' => null, - 'category' => null, - 'account' => $sourceAccount, - 'transaction_journal' => $journal, - 'reconciled' => $data['reconciled'], - 'identifier' => $data['identifier'], - ]; - $source = $this->create($sourceTransactionData); - + $source = $this->create( + [ + 'description' => $description, + 'amount' => app('steam')->negative(strval($data['amount'])), + 'foreign_amount' => null, + 'currency' => $currency, + 'account' => $sourceAccount, + 'transaction_journal' => $journal, + 'reconciled' => $data['reconciled'], + 'identifier' => $data['identifier'], + ] + ); // then make a "positive" transaction based on the data in the array. - $destTransactionData = [ - 'description' => $sourceTransactionData['description'], - 'amount' => app('steam')->positive(strval($data['amount'])), - 'foreign_amount' => null, - 'currency' => $currency, - 'foreign_currency' => null, - 'budget' => null, - 'category' => null, - 'account' => $destinationAccount, - 'transaction_journal' => $journal, - 'reconciled' => $data['reconciled'], - 'identifier' => $data['identifier'], - ]; - $dest = $this->create($destTransactionData); + $dest = $this->create( + [ + 'description' => $description, + 'amount' => app('steam')->positive(strval($data['amount'])), + 'foreign_amount' => null, + 'currency' => $currency, + 'account' => $destinationAccount, + 'transaction_journal' => $journal, + 'reconciled' => $data['reconciled'], + 'identifier' => $data['identifier'], + ] + ); + // set foreign currency + $foreign = $this->findCurrency($data['foreign_currency_id'], $data['foreign_currency_code']); + $this->setForeignCurrency($source, $foreign); + $this->setForeignCurrency($dest, $foreign); + + // set foreign amount: + if (!is_null($data['foreign_amount'])) { + $this->setForeignAmount($source, app('steam')->negative(strval($data['foreign_amount']))); + $this->setForeignAmount($dest, app('steam')->positive(strval($data['foreign_amount']))); + } + + // set budget: + $budget = $this->findBudget($data['budget_id'], $data['budget_name']); + $this->setBudget($source, $budget); + $this->setBudget($dest, $budget); + + // set category + $category = $this->findCategory($data['category_id'], $data['category_name']); + $this->setCategory($source, $category); + $this->setCategory($dest, $category); return new Collection([$source, $dest]); } @@ -356,4 +365,57 @@ class TransactionFactory return null; } + + /** + * @param Transaction $transaction + * @param Budget|null $budget + */ + protected function setBudget(Transaction $transaction, ?Budget $budget): void + { + if (is_null($budget)) { + return; + } + $transaction->budgets()->save($budget); + + return; + } + + /** + * @param Transaction $transaction + * @param Category|null $category + */ + protected function setCategory(Transaction $transaction, ?Category $category): void + { + if (is_null($category)) { + return; + } + $transaction->categories()->save($category); + + return; + } + + /** + * @param Transaction $transaction + * @param string $amount + */ + protected function setForeignAmount(Transaction $transaction, string $amount): void + { + $transaction->foreign_amount = $amount; + $transaction->save(); + } + + /** + * @param Transaction $transaction + * @param TransactionCurrency|null $currency + */ + protected function setForeignCurrency(Transaction $transaction, ?TransactionCurrency $currency): void + { + if (is_null($currency)) { + return; + } + $transaction->foreign_currency_id = $currency->id; + $transaction->save(); + + return; + } } \ No newline at end of file diff --git a/app/Factory/TransactionJournalFactory.php b/app/Factory/TransactionJournalFactory.php index 0632703b72..1a5d7b4c31 100644 --- a/app/Factory/TransactionJournalFactory.php +++ b/app/Factory/TransactionJournalFactory.php @@ -25,6 +25,7 @@ namespace FireflyIII\Factory; use FireflyIII\Exceptions\FireflyException; use FireflyIII\Models\Bill; +use FireflyIII\Models\Note; use FireflyIII\Models\PiggyBank; use FireflyIII\Models\TransactionJournal; use FireflyIII\Models\TransactionType; @@ -95,11 +96,31 @@ class TransactionJournalFactory /** @var array $trData */ foreach ($data['transactions'] as $trData) { - $trData['reconciled'] = $data['reconciled'] ?? false; $factory->createPair($journal, $trData); } $this->repository->markCompleted($journal); + // link bill: + $this->connectBill($journal, $data); + + // link piggy bank: + $this->connectPiggyBank($journal, $data); + + // link tags: + $this->connectTags($journal, $data); + + // store note: + $this->storeNote($journal, $data['notes']); + + // store date meta fields (if present): + $this->storeMeta($journal, $data, 'interest_date'); + $this->storeMeta($journal, $data, 'book_date'); + $this->storeMeta($journal, $data, 'process_date'); + $this->storeMeta($journal, $data, 'due_date'); + $this->storeMeta($journal, $data, 'payment_date'); + $this->storeMeta($journal, $data, 'invoice_date'); + $this->storeMeta($journal, $data, 'internal_reference'); + return $journal; } @@ -116,6 +137,49 @@ class TransactionJournalFactory $this->piggyRepository->setUser($user); } + /** + * Connect bill if present. + * + * @param TransactionJournal $journal + * @param array $data + */ + protected function connectBill(TransactionJournal $journal, array $data): void + { + $bill = $this->findBill($data['bill_id'], $data['bill_name']); + if (!is_null($bill)) { + $journal->bill_id = $bill->id; + $journal->save(); + } + } + + /** + * @param TransactionJournal $journal + * @param array $data + */ + protected function connectPiggyBank(TransactionJournal $journal, array $data): void + { + $piggyBank = $this->findPiggyBank($data['piggy_bank_id'], $data['piggy_bank_name']); + if (!is_null($piggyBank)) { + /** @var PiggyBankEventFactory $factory */ + $factory = app(PiggyBankEventFactory::class); + $factory->create($journal, $piggyBank); + } + } + + /** + * @param TransactionJournal $journal + * @param array $data + */ + protected function connectTags(TransactionJournal $journal, array $data): void + { + $factory = app(TagFactory::class); + $factory->setUser($journal->user); + foreach ($data['tags'] as $string) { + $tag = $factory->findOrCreate($string); + $journal->tags()->save($tag); + } + } + /** * Find the given bill based on the ID or the name. ID takes precedence over the name. * @@ -203,4 +267,39 @@ class TransactionJournalFactory return $transactionType; } + /** + * @param TransactionJournal $journal + * @param array $data + * @param string $field + */ + protected function storeMeta(TransactionJournal $journal, array $data, string $field): void + { + $value = $data[$field]; + if (!is_null($value)) { + $set = [ + 'journal' => $journal, + 'name' => $field, + 'data' => $data[$field], + ]; + /** @var TransactionJournalMetaFactory $factory */ + $factory = app(TransactionJournalMetaFactory::class); + $factory->updateOrCreate($set); + } + } + + /** + * @param TransactionJournal $journal + * @param string $notes + */ + protected function storeNote(TransactionJournal $journal, string $notes): void + { + if (strlen($notes) > 0) { + $note = new Note; + $note->noteable()->associate($journal); + $note->text = $notes; + $note->save(); + } + + } + } \ No newline at end of file diff --git a/app/Factory/TransactionJournalMetaFactory.php b/app/Factory/TransactionJournalMetaFactory.php new file mode 100644 index 0000000000..af4814da39 --- /dev/null +++ b/app/Factory/TransactionJournalMetaFactory.php @@ -0,0 +1,58 @@ +. + */ + +declare(strict_types=1); + +namespace FireflyIII\Factory; + +use Carbon\Carbon; +use FireflyIII\Models\TransactionJournalMeta; + +/** + * Class TransactionJournalMetaFactory + */ +class TransactionJournalMetaFactory +{ + /** + * @param array $data + * + * @return TransactionJournalMeta + */ + public function updateOrCreate(array $data): TransactionJournalMeta + { + $value = $data['data']; + if ($data['data'] instanceof Carbon) { + $value = $data['data']->toW3cString(); + } + + $entry = $data['journal']->transactionJournalMeta()->where('name', $data['name'])->first(); + if (null === $entry) { + $entry = new TransactionJournalMeta(); + $entry->transactionJournal()->associate($data['journal']); + $entry->name = $data['name']; + } + $entry->data = $value; + $entry->save(); + + return $entry; + } + +} \ No newline at end of file diff --git a/app/Models/Transaction.php b/app/Models/Transaction.php index 22569dd484..09ab60ab42 100644 --- a/app/Models/Transaction.php +++ b/app/Models/Transaction.php @@ -91,7 +91,7 @@ class Transaction extends Model */ protected $fillable = ['account_id', 'transaction_journal_id', 'description', 'amount', 'identifier', 'transaction_currency_id', 'foreign_currency_id', - 'foreign_amount',]; + 'foreign_amount','reconciled']; /** * @var array */ diff --git a/app/Transformers/TransactionTransformer.php b/app/Transformers/TransactionTransformer.php index ad91f99cc6..ac4386ac7e 100644 --- a/app/Transformers/TransactionTransformer.php +++ b/app/Transformers/TransactionTransformer.php @@ -25,6 +25,7 @@ namespace FireflyIII\Transformers; use FireflyIII\Exceptions\FireflyException; +use FireflyIII\Models\Note; use FireflyIII\Models\Transaction; use FireflyIII\Models\TransactionType; use League\Fractal\Resource\Collection as FractalCollection; @@ -167,6 +168,13 @@ class TransactionTransformer extends TransformerAbstract $budgetName = is_null($transaction->transaction_budget_name) ? $transaction->transaction_journal_budget_name : $transaction->transaction_budget_name; } + /** @var Note $dbNote */ + $dbNote = $transaction->transactionJournal->notes()->first(); + $notes = null; + if(!is_null($dbNote)) { + $notes = $dbNote->text; + } + $data = [ 'id' => (int)$transaction->id, 'updated_at' => $transaction->updated_at->toAtomString(), @@ -191,6 +199,7 @@ class TransactionTransformer extends TransformerAbstract 'category_name' => $categoryName, 'budget_id' => $budgetId, 'budget_name' => $budgetName, + 'notes' => $notes, 'links' => [ [ 'rel' => 'self', diff --git a/app/Validation/FireflyValidator.php b/app/Validation/FireflyValidator.php index 1340b7c9a7..ff07c8321b 100644 --- a/app/Validation/FireflyValidator.php +++ b/app/Validation/FireflyValidator.php @@ -212,9 +212,9 @@ class FireflyValidator extends Validator */ public function validateMore($attribute, $value, $parameters): bool { - $compare = $parameters[0] ?? '0'; + $compare = strval($parameters[0] ?? '0'); - return bccomp($value, $compare) > 0; + return bccomp(strval($value), $compare) > 0; } /** diff --git a/tests/Api/V1/Controllers/TransactionControllerTest.php b/tests/Api/V1/Controllers/TransactionControllerTest.php index dfc2d09b34..a830b1c5b4 100644 --- a/tests/Api/V1/Controllers/TransactionControllerTest.php +++ b/tests/Api/V1/Controllers/TransactionControllerTest.php @@ -26,6 +26,7 @@ namespace Tests\Api\V1\Controllers; use FireflyIII\Helpers\Collector\JournalCollector; use FireflyIII\Helpers\Collector\JournalCollectorInterface; +use FireflyIII\Models\TransactionCurrency; use FireflyIII\Models\TransactionJournal; use FireflyIII\Repositories\Journal\JournalRepositoryInterface; use Illuminate\Support\Collection; @@ -33,6 +34,11 @@ use Laravel\Passport\Passport; use Tests\TestCase; /** + * todo test bad budget, bad category + * todo test bad piggy, bad bill + * todo test fire of rules with parameter + * todo test bad currency, bad foreign currency + * todo test reconciled, identifier * Class TransactionControllerTest */ class TransactionControllerTest extends TestCase @@ -73,6 +79,7 @@ class TransactionControllerTest extends TestCase /** * @covers \FireflyIII\Api\V1\Controllers\TransactionController::__construct * @covers \FireflyIII\Api\V1\Controllers\TransactionController::index + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::mapTypes * * @throws \FireflyIII\Exceptions\FireflyException */ @@ -117,6 +124,7 @@ class TransactionControllerTest extends TestCase /** * @covers \FireflyIII\Api\V1\Controllers\TransactionController::__construct * @covers \FireflyIII\Api\V1\Controllers\TransactionController::index + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::mapTypes * @throws \FireflyIII\Exceptions\FireflyException */ public function testIndexWithRange() @@ -429,6 +437,383 @@ class TransactionControllerTest extends TestCase ); } + /** + * Submit with existing budget ID, see it reflected in output. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreBudgetId() + { + $budget = auth()->user()->budgets()->first(); + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + 'budget_id' => $budget->id, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + 'budget_id' => $budget->id, + 'budget_name' => $budget->name, + ], + 'links' => true, + ], + ] + ); + } + + /** + * Submit with existing budget name, see it reflected in output. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreBudgetName() + { + $budget = auth()->user()->budgets()->first(); + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + 'budget_name' => $budget->name, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + 'budget_id' => $budget->id, + 'budget_name' => $budget->name, + ], + 'links' => true, + ], + ] + ); + } + + /** + * Submit with existing category ID, see it reflected in output. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreCategoryID() + { + $category = auth()->user()->categories()->first(); + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + 'category_id' => $category->id, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + 'category_id' => $category->id, + 'category_name' => $category->name, + ], + 'links' => true, + ], + ] + ); + } + + /** + * Submit with existing category ID, see it reflected in output. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreCategoryName() + { + $category = auth()->user()->categories()->first(); + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + 'category_name' => $category->name, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + 'category_id' => $category->id, + 'category_name' => $category->name, + ], + 'links' => true, + ], + ] + ); + } + + /** + * Add foreign amount information. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreForeignAmount() + { + $currency = TransactionCurrency::first(); + $foreign = TransactionCurrency::where('id', '!=', $currency->id)->first(); + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => $currency->id, + 'foreign_currency_id' => $foreign->id, + 'foreign_amount' => 23, + 'source_id' => $account->id, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'currency_code' => $currency->code, + 'foreign_currency_code' => $foreign->code, + 'foreign_amount' => -23, + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + ], + 'links' => true, + ], + ] + ); + } + + /** + * Add all available meta data fields. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreMetaData() + { + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + // store date meta fields (if present): + 'interest_date' => '2017-08-02', + 'book_date' => '2017-08-03', + 'process_date' => '2017-08-04', + 'due_date' => '2017-08-05', + 'payment_date' => '2017-08-06', + 'invoice_date' => '2017-08-07', + 'internal_reference' => 'I are internal ref!', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions?include=journal_meta', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertSee('interest_date'); + $response->assertSee('book_date'); + $response->assertSee('process_date'); + $response->assertSee('due_date'); + $response->assertSee('payment_date'); + $response->assertSee('invoice_date'); + $response->assertSee('internal_reference'); + $response->assertSee('2017-08-02'); + $response->assertSee('2017-08-03'); + $response->assertSee('2017-08-04'); + $response->assertSee('2017-08-05'); + $response->assertSee('2017-08-06'); + $response->assertSee('2017-08-07'); + $response->assertSee('I are internal ref!'); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + ], + 'links' => true, + ], + ] + ); + } + + /** + * Submit the minimum amount of data required to create a withdrawal. + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreNotes() + { + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'notes' => 'I am a note', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertSee('I am a note'); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + 'notes' => 'I am a note', + ], + 'links' => true, + ], + ] + ); + } + /** * Submit the minimum amount of data required to create a withdrawal. * When sending a piggy bank by name, this must be reflected in the output. @@ -546,6 +931,55 @@ class TransactionControllerTest extends TestCase ); } + /** + * Set a different reconciled var + * + * @covers \FireflyIII\Api\V1\Controllers\TransactionController::store + */ + public function testSuccessStoreReconciled() + { + $account = auth()->user()->accounts()->where('account_type_id', 3)->first(); + $data = [ + 'description' => 'Some transaction #' . rand(1, 1000), + 'date' => '2018-01-01', + 'type' => 'withdrawal', + 'transactions' => [ + [ + 'amount' => '10', + 'currency_id' => 1, + 'source_id' => $account->id, + 'reconciled' => true, + ], + + + ], + ]; + + // test API + $response = $this->post('/api/v1/transactions', $data, ['Accept' => 'application/json']); + $response->assertStatus(200); + $response->assertJson( + [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ + 'description' => $data['description'], + 'date' => $data['date'], + 'source_id' => $account->id, + 'source_name' => $account->name, + 'type' => 'Withdrawal', + 'source_type' => 'Asset account', + 'destination_name' => 'Cash account', + 'destination_type' => 'Cash account', + 'amount' => -10, + 'reconciled' => true, + ], + 'links' => true, + ], + ] + ); + } + /** * Submit the minimum amount of data required to create a withdrawal. * Add some tags as well. Expect to see them in the result. @@ -584,9 +1018,9 @@ class TransactionControllerTest extends TestCase } $response->assertJson( [ - 'data' => [ - 'type' => 'transactions', - 'attributes' => [ + 'data' => [ + 'type' => 'transactions', + 'attributes' => [ 'description' => $data['description'], 'date' => $data['date'], 'source_id' => $account->id, @@ -597,9 +1031,10 @@ class TransactionControllerTest extends TestCase 'destination_type' => 'Cash account', 'amount' => -10, ], - 'links' => true, - 'includes' => [], + 'links' => [], + 'relationships' => [], ], + 'included' => [], ] ); }