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:
David Bomba 2020-05-12 19:56:30 +10:00 committed by GitHub
parent 1f846e3136
commit 6d0d6c10cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 322 additions and 20 deletions

View File

@ -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;
@ -45,6 +46,8 @@ class Kernel extends ConsoleKernel
$schedule->job(new ReminderJob)->daily();
$schedule->job(new UpdateExchangeRates)->daily();
/* Run hosted specific jobs */
if(Ninja::isHosted()) {
$schedule->job(new AdjustEmailQuota())->daily();

View File

@ -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;

View 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);
}
}

View File

@ -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();
$usd_amount = $this->convertToUsd($amount, $from_currency);
return $exchangeRates->convert($amount, $from_currency, $to_currency, $date);
return $this->convertFromUsdToCurrency($usd_amount, $to_currency);
}
public function exchangeRate($from_currency_id, $to_currency_id, $date = null)
{
if(!$date)
$date = Carbon::now();
$from_currency = Currency::find($from_currency_id);
$from_currency = Currency::find($from_currency_id)->code;
$to_currency = Currency::find($to_currency_id);
$to_currency = Currency::find($to_currency_id)->code;
$usd_amount = $this->convertToUsd(1, $from_currency);
$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;
}
}

View File

@ -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;
/**

View File

@ -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
View 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);
});
}
}

View File

@ -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"

View File

@ -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'

View File

@ -56,7 +56,6 @@ class PreviewTest extends TestCase
];
$response = $this->withHeaders([
'X-API-SECRET' => config('ninja.api_secret'),
'X-API-TOKEN' => $this->token

View 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);
}
}