mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Working on OAuth (#3686)
* Update Exchange rate Data once a day * Tests for currency conversions * Fixes for tests * Fix for adding blank product keys * Class for logging emails sent * fixes for oauth
This commit is contained in:
parent
1f846e3136
commit
6d0d6c10cd
@ -15,6 +15,7 @@ use App\Jobs\Cron\RecurringInvoicesCron;
|
||||
use App\Jobs\Ninja\AdjustEmailQuota;
|
||||
use App\Jobs\Util\ReminderJob;
|
||||
use App\Jobs\Util\SendFailedEmails;
|
||||
use App\Jobs\Util\UpdateExchangeRates;
|
||||
use App\Jobs\Util\VersionCheck;
|
||||
use App\Utils\Ninja;
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
@ -44,6 +45,8 @@ class Kernel extends ConsoleKernel
|
||||
$schedule->job(new VersionCheck)->daily();
|
||||
|
||||
$schedule->job(new ReminderJob)->daily();
|
||||
|
||||
$schedule->job(new UpdateExchangeRates)->daily();
|
||||
|
||||
/* Run hosted specific jobs */
|
||||
if(Ninja::isHosted()) {
|
||||
|
@ -58,6 +58,10 @@ class UpdateOrCreateProduct implements ShouldQueue
|
||||
MultiDB::setDB($this->company->db);
|
||||
|
||||
foreach ($this->products as $item) {
|
||||
|
||||
if(empty($item->product_key))
|
||||
continue;
|
||||
|
||||
$product = Product::firstOrNew(['product_key' => $item->product_key, 'company_id' => $this->invoice->company->id]);
|
||||
|
||||
$product->product_key = $item->product_key;
|
||||
|
80
app/Jobs/Util/UpdateExchangeRates.php
Normal file
80
app/Jobs/Util/UpdateExchangeRates.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Jobs\Util;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Models\Currency;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
class UpdateExchangeRates implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
|
||||
if(config('ninja.db.multi_db_enabled'))
|
||||
{
|
||||
|
||||
foreach (MultiDB::$dbs as $db) {
|
||||
MultiDB::setDB($db);
|
||||
$this->updateCurrencies();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
$this->updateCurrencies();
|
||||
|
||||
}
|
||||
|
||||
private function updateCurrencies()
|
||||
{
|
||||
|
||||
if(empty(config('ninja.currency_converter_api_key')))
|
||||
return;
|
||||
|
||||
$cc_endpoint = sprintf('https://openexchangerates.org/api/latest.json?app_id=%s', config('ninja.currency_converter_api_key'));
|
||||
|
||||
$client = new \GuzzleHttp\Client();
|
||||
$response = $client->get($cc_endpoint);
|
||||
|
||||
$currency_api = json_decode($response->getBody());
|
||||
|
||||
/* Update all currencies */
|
||||
Currency::all()->each(function ($currency) use($currency_api){
|
||||
|
||||
$currency->exchange_rate = $currency_api->rates->{$currency->code};
|
||||
$currency->save();
|
||||
|
||||
});
|
||||
|
||||
/* Rebuild the cache */
|
||||
$currencies = Currency::orderBy('name')->get();
|
||||
|
||||
Cache::forever('currencies', $currencies);
|
||||
}
|
||||
}
|
@ -12,7 +12,6 @@
|
||||
namespace App\Libraries\Currency\Conversion;
|
||||
|
||||
use App\Models\Currency;
|
||||
use AshAllenDesign\LaravelExchangeRates\Classes\ExchangeRate;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class CurrencyApi implements CurrencyConversionInterface
|
||||
@ -24,30 +23,51 @@ class CurrencyApi implements CurrencyConversionInterface
|
||||
if(!$date)
|
||||
$date = Carbon::now();
|
||||
|
||||
$from_currency = Currency::find($from_currency_id)->code;
|
||||
$from_currency = Currency::find($from_currency_id);
|
||||
|
||||
$to_currency = Currency::find($to_currency_id)->code;
|
||||
$to_currency = Currency::find($to_currency_id);
|
||||
|
||||
$exchangeRates = new ExchangeRate();
|
||||
|
||||
return $exchangeRates->convert($amount, $from_currency, $to_currency, $date);
|
||||
$usd_amount = $this->convertToUsd($amount, $from_currency);
|
||||
|
||||
return $this->convertFromUsdToCurrency($usd_amount, $to_currency);
|
||||
|
||||
}
|
||||
|
||||
public function exchangeRate($from_currency_id, $to_currency_id, $date = null)
|
||||
{
|
||||
|
||||
$from_currency = Currency::find($from_currency_id);
|
||||
|
||||
$to_currency = Currency::find($to_currency_id);
|
||||
|
||||
$usd_amount = $this->convertToUsd(1, $from_currency);
|
||||
|
||||
if(!$date)
|
||||
$date = Carbon::now();
|
||||
|
||||
$from_currency = Currency::find($from_currency_id)->code;
|
||||
|
||||
$to_currency = Currency::find($to_currency_id)->code;
|
||||
|
||||
$exchangeRates = new ExchangeRate();
|
||||
|
||||
return $exchangeRates->exchangeRate($from_currency, $to_currency, $date);
|
||||
return $this->convertFromUsdToCurrency($usd_amount, $to_currency);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a currency amount to USD
|
||||
*
|
||||
* @param float $amount amount
|
||||
* @param object $currency currency object
|
||||
* @return float USD Amount
|
||||
*/
|
||||
private function convertToUsd($amount, $currency)
|
||||
{
|
||||
return $amount / $currency->exchange_rate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts USD to any other denomination
|
||||
*
|
||||
* @param float $amount amount
|
||||
* @param object $currency destination currency
|
||||
* @return float the converted amount
|
||||
*/
|
||||
private function convertFromUsdToCurrency($amount, $currency)
|
||||
{
|
||||
return $amount * $currency->exchange_rate;
|
||||
}
|
||||
|
||||
}
|
@ -12,7 +12,7 @@
|
||||
namespace App\Libraries\OAuth;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use App\Ninja\OAuth\Providers\Google;
|
||||
use App\Libraries\OAuth\Providers\Google;
|
||||
use Laravel\Socialite\Facades\Socialite;
|
||||
|
||||
/**
|
||||
|
@ -17,6 +17,8 @@ class Currency extends StaticModel
|
||||
{
|
||||
public $timestamps = false;
|
||||
|
||||
protected $guarded = ['id'];
|
||||
|
||||
protected $casts = [
|
||||
'exchange_rate' => 'float',
|
||||
'swap_currency_symbol' => 'boolean',
|
||||
|
69
app/Utils/EmailStats.php
Normal file
69
app/Utils/EmailStats.php
Normal file
@ -0,0 +1,69 @@
|
||||
<?php
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com)
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2020. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://opensource.org/licenses/AAL
|
||||
*/
|
||||
|
||||
namespace App\Utils;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
|
||||
/**
|
||||
* Class EmailStats.
|
||||
*/
|
||||
class EmailStats
|
||||
{
|
||||
const EMAIL = 'email_';
|
||||
|
||||
/**
|
||||
* Increments the counter for emails sent
|
||||
* for a company
|
||||
* @param string $company_key The company key
|
||||
* @return void
|
||||
*/
|
||||
public static function inc($company_key)
|
||||
{
|
||||
Cache::increment(self::EMAIL . $company_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the email sent count
|
||||
*
|
||||
* @param string $company_key The company key
|
||||
* @return int The number email sent so far 'today'
|
||||
*/
|
||||
public static function count($company_key)
|
||||
{
|
||||
return Cache::get(self::EMAIL . $company_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the cache for the emails sent
|
||||
*
|
||||
* @param string $company_key The company key
|
||||
* @return void
|
||||
*/
|
||||
public static function clear($company_key)
|
||||
{
|
||||
Cache::forget(self::EMAIL . $company_key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through a list of companies
|
||||
* and flushes the email sent data
|
||||
*
|
||||
* @param string $companies The company key
|
||||
* @return void
|
||||
*/
|
||||
public static function clearCompanies($companies)
|
||||
{
|
||||
$companies->each(function ($company) {
|
||||
self::clear($company->company_key);
|
||||
});
|
||||
}
|
||||
}
|
@ -21,7 +21,6 @@
|
||||
"php": ">=7.3",
|
||||
"ext-json": "*",
|
||||
"asgrim/ofxparser": "^1.2",
|
||||
"ashallendesign/laravel-exchange-rates": "^2.1",
|
||||
"beganovich/omnipay-checkout": "dev-master",
|
||||
"cleverit/ubl_invoice": "^1.3",
|
||||
"codedge/laravel-selfupdater": "~3.0",
|
||||
@ -54,7 +53,6 @@
|
||||
"spatie/browsershot": "^3.29",
|
||||
"staudenmeir/eloquent-has-many-deep": "^1.11",
|
||||
"stripe/stripe-php": "^7.0",
|
||||
"superbalist/laravel-google-cloud-storage": "^2.2",
|
||||
"turbo124/beacon": "^0",
|
||||
"webpatser/laravel-countries": "dev-master#75992ad",
|
||||
"yajra/laravel-datatables-oracle": "~9.0"
|
||||
|
@ -24,6 +24,7 @@ return [
|
||||
'error_email' => env('ERROR_EMAIL', ''),
|
||||
'company_id' => 0,
|
||||
'hash_salt' => env('HASH_SALT', ''),
|
||||
'currency_converter_api_key' => env('OPENEXCHANGE_APP_ID',''),
|
||||
|
||||
'environment' => env('NINJA_ENVIRONMENT', 'selfhost'), // 'hosted', 'development', 'selfhost', 'reseller'
|
||||
|
||||
|
@ -56,7 +56,6 @@ class PreviewTest extends TestCase
|
||||
|
||||
];
|
||||
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'X-API-SECRET' => config('ninja.api_secret'),
|
||||
'X-API-TOKEN' => $this->token
|
||||
|
126
tests/Feature/UpdateExchangeRatesTest.php
Normal file
126
tests/Feature/UpdateExchangeRatesTest.php
Normal file
@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\DataMapper\DefaultSettings;
|
||||
use App\Jobs\Util\UpdateExchangeRates;
|
||||
use App\Libraries\Currency\Conversion\CurrencyApi;
|
||||
use App\Models\Account;
|
||||
use App\Models\Client;
|
||||
use App\Models\ClientContact;
|
||||
use App\Models\Company;
|
||||
use App\Models\Currency;
|
||||
use App\Models\User;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use AshAllenDesign\LaravelExchangeRates\Facades\ExchangeRate;
|
||||
use Faker\Factory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Foundation\Testing\WithFaker;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Tests\MockAccountData;
|
||||
use Tests\TestCase;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @covers App\Jobs\Util\UpdateExchangeRates
|
||||
*/
|
||||
class UpdateExchangeRatesTest extends TestCase
|
||||
{
|
||||
use MakesHash;
|
||||
use DatabaseTransactions;
|
||||
use MockAccountData;
|
||||
|
||||
public function setUp() :void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->makeTestData();
|
||||
|
||||
Session::start();
|
||||
|
||||
$this->faker = \Faker\Factory::create();
|
||||
|
||||
Model::reguard();
|
||||
}
|
||||
|
||||
|
||||
public function testExchangeRate()
|
||||
{
|
||||
|
||||
if(!empty(config('ninja.currency_converter_api_key')))
|
||||
{
|
||||
$cc_endpoint = sprintf('https://openexchangerates.org/api/latest.json?app_id=%s', config('ninja.currency_converter_api_key'));
|
||||
|
||||
$client = new \GuzzleHttp\Client();
|
||||
$response = $client->get($cc_endpoint);
|
||||
|
||||
$currency_api = json_decode($response->getBody());
|
||||
|
||||
UpdateExchangeRates::dispatchNow();
|
||||
|
||||
$currencies = Cache::get('currencies');
|
||||
|
||||
$gbp_currency = $currencies->filter(function ($item) {
|
||||
return $item->id == 2;
|
||||
})->first();
|
||||
|
||||
$this->assertEquals($currency_api->rates->GBP, $gbp_currency->exchange_rate);
|
||||
}
|
||||
else
|
||||
$this->markTestSkipped('No API Key set');
|
||||
|
||||
}
|
||||
|
||||
public function testExchangeRateConversion()
|
||||
{
|
||||
$usd = Currency::find(1);
|
||||
$gbp = Currency::find(2);
|
||||
|
||||
|
||||
$usd->exchange_rate = 1;
|
||||
$usd->save();
|
||||
|
||||
$gbp->exchange_rate = 0.5;
|
||||
$gbp->save();
|
||||
|
||||
$currency_api = new CurrencyApi();
|
||||
|
||||
$convert_to_gbp = $currency_api->convert(10, 1, 2);
|
||||
|
||||
$this->assertEquals($convert_to_gbp, 5);
|
||||
}
|
||||
|
||||
public function testSyntheticExchangeRate()
|
||||
{
|
||||
$usd = Currency::find(1);
|
||||
$gbp = Currency::find(2);
|
||||
$aud = Currency::find(12);
|
||||
|
||||
$usd->exchange_rate = 1;
|
||||
$usd->save();
|
||||
|
||||
$gbp->exchange_rate = 0.5;
|
||||
$gbp->save();
|
||||
|
||||
$aud->exchange_rate = 1.5;
|
||||
$aud->save();
|
||||
|
||||
$currency_api = new CurrencyApi();
|
||||
|
||||
$convert_to_aud = $currency_api->convert(10, 1, 12);
|
||||
|
||||
$this->assertEquals($convert_to_aud, 15);
|
||||
|
||||
$synthetic_exchange = $currency_api->exchangeRate($gbp->id, $aud->id);
|
||||
|
||||
$this->assertEquals($synthetic_exchange, 3);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user