First basic code for currency exchange rate routines.

This commit is contained in:
James Cole
2017-04-13 20:47:59 +02:00
parent 8db96025a3
commit 994542c75d
11 changed files with 397 additions and 32 deletions

View File

@@ -0,0 +1,60 @@
<?php
/**
* ExchangeController.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Json;
use Carbon\Carbon;
use FireflyIII\Http\Controllers\Controller;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\Repositories\Currency\CurrencyRepositoryInterface;
use FireflyIII\Services\Currency\ExchangeRateInterface;
use Illuminate\Http\Request;
use Response;
/**
* Class ExchangeController
*
* @package FireflyIII\Http\Controllers\Json
*/
class ExchangeController extends Controller
{
/**
* @param Request $request
* @param TransactionCurrency $fromCurrency
* @param TransactionCurrency $toCurrency
* @param Carbon $date
*/
public function getRate(Request $request, TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date)
{
/** @var CurrencyRepositoryInterface $repository */
$repository = app(CurrencyRepositoryInterface::class);
$rate = $repository->getExchangeRate($fromCurrency, $toCurrency, $date);
$amount = null;
if (is_null($rate->id)) {
$preferred = env('EXCHANGE_RATE_SERVICE', config('firefly.preferred_exchange_service'));
$class = config('firefly.currency_exchange_services.' . $preferred);
/** @var ExchangeRateInterface $object */
$object = app($class);
$object->setUser(auth()->user());
$rate = $object->getRate($fromCurrency, $toCurrency, $date);
}
$return = $rate->toArray();
$return['amount'] = null;
if (!is_null($request->get('amount'))) {
// assume amount is in "from" currency:
$return['amount'] = bcmul($request->get('amount'), $rate->rate);
}
return Response::json($return);
}
}

View File

@@ -0,0 +1,53 @@
<?php
/**
* CurrencyExchange.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Models;
use FireflyIII\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
/**
* Class CurrencyExchange
*
* @package FireflyIII\Models
*/
class CurrencyExchangeRate extends Model
{
protected $dates = ['created_at', 'updated_at', 'date'];
/**
* @return BelongsTo
*/
public function fromCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class, 'from_currency_id');
}
/**
* @return BelongsTo
*/
public function toCurrency(): BelongsTo
{
return $this->belongsTo(TransactionCurrency::class, 'to_currency_id');
}
/**
* @return BelongsTo
*/
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

View File

@@ -14,6 +14,8 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Currency;
use Carbon\Carbon;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\User;
@@ -178,6 +180,28 @@ class CurrencyRepository implements CurrencyRepositoryInterface
return $preferred;
}
/**
* @param TransactionCurrency $fromCurrency
* @param TransactionCurrency $toCurrency
* @param Carbon $date
*
* @return CurrencyExchangeRate
*/
public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate
{
$rate = $this->user->currencyExchangeRates()
->where('from_currency_id', $fromCurrency->id)
->where('to_currency_id', $toCurrency->id)
->where('date', $date->format('Y-m-d'))->first();
if (!is_null($rate)) {
return $rate;
}
return new CurrencyExchangeRate;
}
/**
* @param User $user
*/

View File

@@ -14,6 +14,8 @@ declare(strict_types=1);
namespace FireflyIII\Repositories\Currency;
use Carbon\Carbon;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\Preference;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\User;
@@ -95,6 +97,15 @@ interface CurrencyRepositoryInterface
*/
public function getCurrencyByPreference(Preference $preference): TransactionCurrency;
/**
* @param TransactionCurrency $fromCurrency
* @param TransactionCurrency $toCurrency
* @param Carbon $date
*
* @return CurrencyExchangeRate
*/
public function getExchangeRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate;
/**
* @param User $user
*/

View File

@@ -0,0 +1,38 @@
<?php
/**
* ExchangeRateInterface.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Services\Currency;
use Carbon\Carbon;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\User;
interface ExchangeRateInterface
{
/**
* @param TransactionCurrency $fromCurrency
* @param TransactionCurrency $toCurrency
* @param Carbon $date
*
* @return CurrencyExchangeRate
*/
public function getRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate;
/**
* @param User $user
*
* @return mixed
*/
public function setUser(User $user);
}

View File

@@ -0,0 +1,69 @@
<?php
/**
* FixerIO.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Services\Currency;
use Carbon\Carbon;
use FireflyIII\Models\CurrencyExchangeRate;
use FireflyIII\Models\TransactionCurrency;
use FireflyIII\User;
use Log;
use Requests;
/**
* Class FixerIO
*
* @package FireflyIII\Services\Currency
*/
class FixerIO implements ExchangeRateInterface
{
/** @var User */
protected $user;
public function getRate(TransactionCurrency $fromCurrency, TransactionCurrency $toCurrency, Carbon $date): CurrencyExchangeRate
{
$uri = sprintf('https://api.fixer.io/%s?base=%s&symbols=%s', $date->format('Y-m-d'), $fromCurrency->code, $toCurrency->code);
$result = Requests::get($uri);
$rate = 1.0;
$content = null;
if ($result->status_code !== 200) {
Log::error(sprintf('Something went wrong. Received error code %d and body "%s" from FixerIO.', $result->status_code, $result->body));
}
// get rate from body:
if ($result->status_code === 200) {
$content = json_decode($result->body, true);
}
if (!is_null($content)) {
$code = $toCurrency->code;
$rate = isset($content['rates'][$code]) ? $content['rates'][$code] : '1';
}
// create new currency exchange rate object:
$exchangeRate = new CurrencyExchangeRate;
$exchangeRate->user()->associate($this->user);
$exchangeRate->fromCurrency()->associate($fromCurrency);
$exchangeRate->toCurrency()->associate($toCurrency);
$exchangeRate->date = $date;
$exchangeRate->rate = $rate;
$exchangeRate->save();
return $exchangeRate;
}
/**
* @param User $user
*/
public function setUser(User $user)
{
$this->user = $user;
}
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* CurrencyCode.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Support\Binder;
use FireflyIII\Models\TransactionCurrency;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
/**
* Class CurrencyCode
*
* @package FireflyIII\Support\Binder
*/
class CurrencyCode implements BinderInterface
{
/**
* @param $value
* @param $route
*
* @return mixed
*/
public static function routeBinder($value, $route)
{
$currency = TransactionCurrency::where('code', $value)->first();
if (!is_null($currency)) {
return $currency;
}
throw new NotFoundHttpException;
}
}

View File

@@ -15,6 +15,7 @@ declare(strict_types=1);
namespace FireflyIII;
use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Models\CurrencyExchangeRate;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
@@ -119,6 +120,14 @@ class User extends Authenticatable
return $this->hasMany('FireflyIII\Models\Category');
}
/**
* @return HasMany
*/
public function currencyExchangeRates(): HasMany
{
return $this->hasMany(CurrencyExchangeRate::class);
}
/**
* @return HasMany
*/

View File

@@ -134,6 +134,8 @@ return [
'category' => 'FireflyIII\Models\Category',
'transaction_type' => 'FireflyIII\Models\TransactionType',
'currency' => 'FireflyIII\Models\TransactionCurrency',
'fromCurrencyCode' => 'FireflyIII\Support\Binder\CurrencyCode',
'toCurrencyCode' => 'FireflyIII\Support\Binder\CurrencyCode',
'limitrepetition' => 'FireflyIII\Models\LimitRepetition',
'budgetlimit' => 'FireflyIII\Models\BudgetLimit',
'piggyBank' => 'FireflyIII\Models\PiggyBank',
@@ -207,4 +209,10 @@ return [
'search_modifiers' => ['amount_is', 'amount', 'amount_max', 'amount_min', 'amount_less', 'amount_more', 'source', 'destination', 'category',
'budget', 'bill', 'type', 'date', 'date_before', 'date_after', 'on', 'before', 'after'],
// tag notes has_attachments
'currency_exchange_services' => [
'fixerio' => 'FireflyIII\Services\Currency\FixerIO',
'openexchangerates' => 'FireflyIII\Services\Currency\OpenExchangeRates',
],
'preferred_exchange_service' => 'fixerio',
];

View File

@@ -0,0 +1,51 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
/**
* Class ChangesForV440
*/
class ChangesForV440 extends Migration
{
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
if (Schema::hasTable('currency_exchange_rates')) {
Schema::drop('currency_exchange_rates');
}
}
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
if (!Schema::hasTable('currency_exchange_rates')) {
Schema::create(
'currency_exchange_rates', function (Blueprint $table) {
$table->increments('id');
$table->timestamps();
$table->softDeletes();
$table->integer('user_id', false, true);
$table->integer('from_currency_id', false, true);
$table->integer('to_currency_id', false, true);
$table->date('date');
$table->decimal('rate', 22, 12);
$table->decimal('user_rate', 22, 12)->nullable();
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
$table->foreign('from_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade');
$table->foreign('to_currency_id')->references('id')->on('transaction_currencies')->onDelete('cascade');
}
);
}
}
}

View File

@@ -414,7 +414,7 @@ Route::group(
);
/**
* JSON Controller
* JSON Controller(s)
*/
Route::group(
['middleware' => 'user-full-auth', 'prefix' => 'json', 'as' => 'json.'], function () {
@@ -437,6 +437,9 @@ Route::group(
Route::post('end-tour', ['uses' => 'JsonController@endTour', 'as' => 'end-tour']);
// currency conversion:
Route::get('rate/{fromCurrencyCode}/{toCurrencyCode}/{date}', ['uses' => 'Json\ExchangeController@getRate', 'as' => 'rate']);
}
);