mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-07 10:24:31 -04:00
Merge pull request #9942 from turbo124/v5-develop
Sort filters for expense.payment_dates
This commit is contained in:
commit
18864e06be
@ -218,6 +218,11 @@ class ExpenseFilters extends QueryFilters
|
|||||||
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
->whereColumn('expense_categories.id', 'expenses.category_id'), $sort_col[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($sort_col[0] == 'payment_date' && in_array($sort_col[1], ['asc', 'desc'])) {
|
||||||
|
return $this->builder
|
||||||
|
->orderByRaw('ISNULL(payment_date), payment_date '. $sort_col[1]);
|
||||||
|
}
|
||||||
|
|
||||||
if($sort_col[0] == 'number') {
|
if($sort_col[0] == 'number') {
|
||||||
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
|
return $this->builder->orderByRaw("REGEXP_REPLACE(number,'[^0-9]+','')+0 " . $dir);
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,30 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
namespace App\Http\Controllers;
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
use App\Http\Requests\Quickbooks\AuthorizedQuickbooksRequest;
|
use App\Http\Requests\Quickbooks\AuthorizedQuickbooksRequest;
|
||||||
use Closure;
|
|
||||||
use App\Utils\Ninja;
|
|
||||||
use App\Models\Company;
|
|
||||||
use App\Libraries\MultiDB;
|
use App\Libraries\MultiDB;
|
||||||
use Illuminate\Support\Str;
|
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Http\Response;
|
|
||||||
use App\Utils\Traits\MakesHash;
|
|
||||||
use App\Jobs\Import\QuickbooksIngest;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\Validator;
|
|
||||||
use App\Http\Requests\Quickbooks\AuthQuickbooksRequest;
|
use App\Http\Requests\Quickbooks\AuthQuickbooksRequest;
|
||||||
use App\Services\Import\Quickbooks\QuickbooksService;
|
use App\Services\Import\Quickbooks\QuickbooksService;
|
||||||
|
|
||||||
class ImportQuickbooksController extends BaseController
|
class ImportQuickbooksController extends BaseController
|
||||||
{
|
{
|
||||||
private array $import_entities = [
|
// private array $import_entities = [
|
||||||
'client' => 'Customer',
|
// 'client' => 'Customer',
|
||||||
'invoice' => 'Invoice',
|
// 'invoice' => 'Invoice',
|
||||||
'product' => 'Item',
|
// 'product' => 'Item',
|
||||||
'payment' => 'Payment'
|
// 'payment' => 'Payment'
|
||||||
];
|
// ];
|
||||||
|
|
||||||
public function onAuthorized(AuthorizedQuickbooksRequest $request)
|
public function onAuthorized(AuthorizedQuickbooksRequest $request)
|
||||||
{
|
{
|
||||||
@ -34,12 +34,11 @@ class ImportQuickbooksController extends BaseController
|
|||||||
$qb = new QuickbooksService($company);
|
$qb = new QuickbooksService($company);
|
||||||
|
|
||||||
$realm = $request->query('realmId');
|
$realm = $request->query('realmId');
|
||||||
$access_token_object = $qb->getAuth()->accessToken($request->query('code'), $realm);
|
$access_token_object = $qb->sdk()->accessTokenFromCode($request->query('code'), $realm);
|
||||||
nlog($access_token_object); //OAuth2AccessToken
|
$qb->sdk()->saveOAuthToken($access_token_object);
|
||||||
$company->quickbooks = $access_token_object;
|
|
||||||
$company->save();
|
return redirect(config('ninja.react_url'));
|
||||||
|
|
||||||
return response()->json(['message' => 'Success'], 200); //todo swapout for redirect to UI
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -53,8 +52,8 @@ class ImportQuickbooksController extends BaseController
|
|||||||
$company = $request->getCompany();
|
$company = $request->getCompany();
|
||||||
$qb = new QuickbooksService($company);
|
$qb = new QuickbooksService($company);
|
||||||
|
|
||||||
$authorizationUrl = $qb->getAuth()->getAuthorizationUrl();
|
$authorizationUrl = $qb->sdk()->getAuthorizationUrl();
|
||||||
$state = $qb->getAuth()->getState();
|
$state = $qb->sdk()->getState();
|
||||||
|
|
||||||
Cache::put($state, $token, 190);
|
Cache::put($state, $token, 190);
|
||||||
|
|
||||||
@ -63,29 +62,29 @@ class ImportQuickbooksController extends BaseController
|
|||||||
|
|
||||||
public function preimport(string $type, string $hash)
|
public function preimport(string $type, string $hash)
|
||||||
{
|
{
|
||||||
// Check for authorization otherwise
|
// // Check for authorization otherwise
|
||||||
// Create a reference
|
// // Create a reference
|
||||||
$data = [
|
// $data = [
|
||||||
'hash' => $hash,
|
// 'hash' => $hash,
|
||||||
'type' => $type
|
// 'type' => $type
|
||||||
];
|
// ];
|
||||||
$this->getData($data);
|
// $this->getData($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getData($data)
|
protected function getData($data)
|
||||||
{
|
{
|
||||||
|
|
||||||
$entity = $this->import_entities[$data['type']];
|
// $entity = $this->import_entities[$data['type']];
|
||||||
$cache_name = "{$data['hash']}-{$data['type']}";
|
// $cache_name = "{$data['hash']}-{$data['type']}";
|
||||||
// TODO: Get or put cache or DB?
|
// // TODO: Get or put cache or DB?
|
||||||
if(! Cache::has($cache_name)) {
|
// if(! Cache::has($cache_name)) {
|
||||||
$contents = call_user_func([$this->service, "fetch{$entity}s"]);
|
// $contents = call_user_func([$this->service, "fetch{$entity}s"]);
|
||||||
if($contents->isEmpty()) {
|
// if($contents->isEmpty()) {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
Cache::put($cache_name, base64_encode($contents->toJson()), 600);
|
// Cache::put($cache_name, base64_encode($contents->toJson()), 600);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,7 +67,7 @@ class ClientTransformer extends BaseTransformer
|
|||||||
}
|
}
|
||||||
|
|
||||||
$transformed_data = $this->preTransform($data);
|
$transformed_data = $this->preTransform($data);
|
||||||
$transformed_data['contacts'][0] = $this->getContacts($data)->toArray() + ['company_id' => $this->company->id ];
|
$transformed_data['contacts'][0] = $this->getContacts($data)->toArray() + ['company_id' => $this->company->id, 'user_id' => $this->company->owner()->id ];
|
||||||
|
|
||||||
return $transformed_data;
|
return $transformed_data;
|
||||||
}
|
}
|
||||||
@ -79,7 +79,9 @@ class ClientTransformer extends BaseTransformer
|
|||||||
'last_name' => $this->getString($data, 'FamilyName'),
|
'last_name' => $this->getString($data, 'FamilyName'),
|
||||||
'phone' => $this->getString($data, 'PrimaryPhone.FreeFormNumber'),
|
'phone' => $this->getString($data, 'PrimaryPhone.FreeFormNumber'),
|
||||||
'email' => $this->getString($data, 'PrimaryEmailAddr.Address'),
|
'email' => $this->getString($data, 'PrimaryEmailAddr.Address'),
|
||||||
'company_id' => $this->company->id
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->company->owner()->id,
|
||||||
|
'send_email' => true,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Import\Quickbooks;
|
|
||||||
|
|
||||||
use App\Repositories\Import\Quickbooks\Repository;
|
|
||||||
use App\Repositories\Import\Quickbooks\Contracts\RepositoryInterface;
|
|
||||||
|
|
||||||
class CustomerRepository extends Repository implements RepositoryInterface
|
|
||||||
{
|
|
||||||
protected string $entity = "Customer";
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Import\Quickbooks;
|
|
||||||
|
|
||||||
use App\Repositories\Import\Quickbooks\Contracts\RepositoryInterface as QuickbooksInterface;
|
|
||||||
|
|
||||||
class InvoiceRepository extends Repository implements QuickbooksInterface
|
|
||||||
{
|
|
||||||
protected string $entity = "Invoice";
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Import\Quickbooks;
|
|
||||||
|
|
||||||
use App\Repositories\Import\Quickbooks\Repository;
|
|
||||||
use App\Repositories\Import\Quickbooks\Contracts\RepositoryInterface;
|
|
||||||
|
|
||||||
class ItemRepository extends Repository implements RepositoryInterface
|
|
||||||
{
|
|
||||||
protected string $entity = "Item";
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Import\Quickbooks;
|
|
||||||
|
|
||||||
use App\Repositories\Import\Quickbooks\Contracts\RepositoryInterface as QuickbooksInterface;
|
|
||||||
|
|
||||||
class PaymentRepository extends Repository implements QuickbooksInterface
|
|
||||||
{
|
|
||||||
protected string $entity = "Payment";
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Repositories\Import\Quickbooks;
|
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use App\Repositories\Import\Quickbooks\Contracts\RepositoryInterface;
|
|
||||||
use App\Services\Import\Quickbooks\Contracts\SdkInterface as QuickbooksInterface;
|
|
||||||
use App\Repositories\Import\Quickbooks\Transformers\Transformer as QuickbooksTransformer;
|
|
||||||
|
|
||||||
abstract class Repository implements RepositoryInterface
|
|
||||||
{
|
|
||||||
protected string $entity;
|
|
||||||
protected QuickbooksInterface $db;
|
|
||||||
protected QuickbooksTransformer $transfomer;
|
|
||||||
|
|
||||||
public function __construct(QuickbooksInterface $db, QuickbooksTransformer $transfomer)
|
|
||||||
{
|
|
||||||
$this->db = $db;
|
|
||||||
$this->transformer = $transfomer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function count(): int
|
|
||||||
{
|
|
||||||
return $this->db->totalRecords($this->entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function all(): Collection
|
|
||||||
{
|
|
||||||
return $this->get($this->count());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get(int $max = 100): Collection
|
|
||||||
{
|
|
||||||
return $this->transformer->transform($this->db->fetchRecords($this->entity, $max), $this->entity);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* Invoice Ninja (https://invoiceninja.com).
|
|
||||||
*
|
|
||||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
|
||||||
*
|
|
||||||
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
|
||||||
*
|
|
||||||
* @license https://www.elastic.co/licensing/elastic-license
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace App\Services\Import\Quickbooks;
|
|
||||||
|
|
||||||
final class Auth
|
|
||||||
{
|
|
||||||
public function __construct(private SdkWrapper $sdk)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public function accessToken(string $code, string $realm): array
|
|
||||||
{
|
|
||||||
// TODO: Get or put token in Cache or DB?
|
|
||||||
return $this->sdk->accessToken($code, $realm);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function refreshToken(): array
|
|
||||||
{
|
|
||||||
// TODO: Get or put token in Cache or DB?
|
|
||||||
return $this->sdk->refreshToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAuthorizationUrl(): string
|
|
||||||
{
|
|
||||||
return $this->sdk->getAuthorizationUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getState(): string
|
|
||||||
{
|
|
||||||
return $this->sdk->getState();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAccessToken(): array
|
|
||||||
{
|
|
||||||
$tokens = [];
|
|
||||||
// $token_store = new CompanyTokensRepository();
|
|
||||||
// $tokens = $token_store->get();
|
|
||||||
// if(empty($tokens)) {
|
|
||||||
// $token = $this->sdk->getAccessToken();
|
|
||||||
// $access_token = $token->getAccessToken();
|
|
||||||
// $realm = $token->getRealmID();
|
|
||||||
// $refresh_token = $token->getRefreshToken();
|
|
||||||
// $access_token_expires = $token->getAccessTokenExpiresAt();
|
|
||||||
// $refresh_token_expires = $token->getRefreshTokenExpiresAt();
|
|
||||||
// $tokens = compact('access_token', 'refresh_token','access_token_expires', 'refresh_token_expires','realm');
|
|
||||||
// }
|
|
||||||
|
|
||||||
return $tokens;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRefreshToken(): array
|
|
||||||
{
|
|
||||||
return $this->getAccessToken();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services\Import\Quickbooks\Contracts;
|
|
||||||
|
|
||||||
interface SdkInterface
|
|
||||||
{
|
|
||||||
public function getAuthorizationUrl(): string;
|
|
||||||
public function accessToken(string $code, string $realm): array;
|
|
||||||
public function refreshToken(): array;
|
|
||||||
public function getAccessToken();
|
|
||||||
public function getRefreshToken(): array;
|
|
||||||
public function totalRecords(string $entity): int;
|
|
||||||
public function fetchRecords(string $entity, int $max): array;
|
|
||||||
}
|
|
@ -12,6 +12,7 @@
|
|||||||
namespace App\Services\Import\Quickbooks;
|
namespace App\Services\Import\Quickbooks;
|
||||||
|
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
|
use QuickBooksOnline\API\Core\CoreConstants;
|
||||||
use QuickBooksOnline\API\DataService\DataService;
|
use QuickBooksOnline\API\DataService\DataService;
|
||||||
|
|
||||||
// quickbooks_realm_id
|
// quickbooks_realm_id
|
||||||
@ -19,32 +20,34 @@ use QuickBooksOnline\API\DataService\DataService;
|
|||||||
// quickbooks_refresh_expires
|
// quickbooks_refresh_expires
|
||||||
class QuickbooksService
|
class QuickbooksService
|
||||||
{
|
{
|
||||||
private DataService $sdk;
|
public DataService $sdk;
|
||||||
|
|
||||||
private Auth $auth;
|
private bool $testMode = true;
|
||||||
|
|
||||||
public function __construct(private Company $company)
|
public function __construct(private Company $company)
|
||||||
{
|
{
|
||||||
$this->init()
|
$this->init();
|
||||||
->auth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function init(): self
|
private function init(): self
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->sdk = DataService::Configure([
|
$config = [
|
||||||
'ClientID' => config('services.quickbooks.client_id'),
|
'ClientID' => config('services.quickbooks.client_id'),
|
||||||
'ClientSecret' => config('services.quickbooks.client_secret'),
|
'ClientSecret' => config('services.quickbooks.client_secret'),
|
||||||
'auth_mode' => 'oauth2',
|
'auth_mode' => 'oauth2',
|
||||||
'scope' => "com.intuit.quickbooks.accounting",
|
'scope' => "com.intuit.quickbooks.accounting",
|
||||||
'RedirectURI' => 'https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl',
|
// 'RedirectURI' => 'https://developer.intuit.com/v2/OAuth2Playground/RedirectUrl',
|
||||||
// 'RedirectURI' => route('quickbooks.authorized'),
|
'RedirectURI' => $this->testMode ? 'https://above-distinctly-teal.ngrok-free.app/quickbooks/authorized' : 'https://invoicing.co/quickbooks/authorized',
|
||||||
]);
|
'baseUrl' => $this->testMode ? CoreConstants::SANDBOX_DEVELOPMENT : CoreConstants::QBO_BASEURL,
|
||||||
|
];
|
||||||
|
|
||||||
// if (env('APP_DEBUG')) {
|
$merged = array_merge($config, $this->ninjaAccessToken());
|
||||||
// $sdk->setLogLocation(storage_path("logs/quickbooks.log"));
|
|
||||||
// $sdk->enableLog();
|
$this->sdk = DataService::Configure($merged);
|
||||||
// }
|
|
||||||
|
$this->sdk->setLogLocation(storage_path("logs/quickbooks.log"));
|
||||||
|
$this->sdk->enableLog();
|
||||||
|
|
||||||
$this->sdk->setMinorVersion("73");
|
$this->sdk->setMinorVersion("73");
|
||||||
$this->sdk->throwExceptionOnError(true);
|
$this->sdk->throwExceptionOnError(true);
|
||||||
@ -52,12 +55,13 @@ class QuickbooksService
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function auth(): self
|
private function ninjaAccessToken()
|
||||||
{
|
{
|
||||||
$wrapper = new SdkWrapper($this->sdk);
|
return isset($this->company->quickbooks->accessTokenKey) ? [
|
||||||
$this->auth = new Auth($wrapper);
|
'accessTokenKey' => $this->company->quickbooks->accessTokenKey,
|
||||||
|
'refreshTokenKey' => $this->company->quickbooks->refresh_token,
|
||||||
return $this;
|
'QBORealmID' => $this->company->quickbooks->realmID,
|
||||||
|
] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSdk(): DataService
|
public function getSdk(): DataService
|
||||||
@ -65,8 +69,9 @@ class QuickbooksService
|
|||||||
return $this->sdk;
|
return $this->sdk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAuth(): Auth
|
public function sdk(): SdkWrapper
|
||||||
{
|
{
|
||||||
return $this->auth;
|
return new SdkWrapper($this->sdk, $this->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,78 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services\Import\Quickbooks\Repositories;
|
|
||||||
|
|
||||||
use App\Models\Company;
|
|
||||||
use App\Libraries\MultiDB;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
|
|
||||||
class CompanyTokensRepository
|
|
||||||
{
|
|
||||||
private $company_key;
|
|
||||||
private $store_key = "quickbooks-token";
|
|
||||||
|
|
||||||
public function __construct(string $key = null)
|
|
||||||
{
|
|
||||||
$this->company_key = $key ?? auth()->user->company()->company_key ?? null;
|
|
||||||
$this->store_key .= $key;
|
|
||||||
$this->setCompanyDbByKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function save(array $tokens)
|
|
||||||
{
|
|
||||||
$this->updateAccessToken($tokens['access_token'], $tokens['access_token_expires']);
|
|
||||||
$this->updateRefreshToken($tokens['refresh_token'], $tokens['refresh_token_expires'], $tokens['realm']);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function findByCompanyKey(): ?Company
|
|
||||||
{
|
|
||||||
return Company::where('company_key', $this->company_key)->first();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setCompanyDbByKey()
|
|
||||||
{
|
|
||||||
MultiDB::findAndSetDbByCompanyKey($this->company_key);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function get()
|
|
||||||
{
|
|
||||||
return $this->getAccessToken() + $this->getRefreshToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected function updateRefreshToken(string $token, string $expires, string $realm)
|
|
||||||
{
|
|
||||||
DB::table('companies')
|
|
||||||
->where('company_key', $this->company_key)
|
|
||||||
->update(['quickbooks_refresh_token' => $token,
|
|
||||||
'quickbooks_realm_id' => $realm,
|
|
||||||
'quickbooks_refresh_expires' => $expires ]);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function updateAccessToken(string $token, string $expires)
|
|
||||||
{
|
|
||||||
|
|
||||||
Cache::put([$this->store_key => $token], $expires);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getAccessToken()
|
|
||||||
{
|
|
||||||
$result = Cache::get($this->store_key);
|
|
||||||
|
|
||||||
return $result ? ['access_token' => $result] : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getRefreshToken()
|
|
||||||
{
|
|
||||||
$result = (array) DB::table('companies')
|
|
||||||
->select('quickbooks_refresh_token', 'quickbooks_realm_id')
|
|
||||||
->where('company_key', $this->company_key)
|
|
||||||
->where('quickbooks_refresh_expires', '>', now())
|
|
||||||
->first();
|
|
||||||
|
|
||||||
return $result ? array_combine(['refresh_token','realm'], array_values($result)) : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,79 +1,162 @@
|
|||||||
<?php
|
<?php
|
||||||
|
/**
|
||||||
|
* Invoice Ninja (https://invoiceninja.com).
|
||||||
|
*
|
||||||
|
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||||
|
*
|
||||||
|
* @copyright Copyright (c) 2024. Invoice Ninja LLC (https://invoiceninja.com)
|
||||||
|
*
|
||||||
|
* @license https://www.elastic.co/licensing/elastic-license
|
||||||
|
*/
|
||||||
|
|
||||||
namespace App\Services\Import\Quickbooks;
|
namespace App\Services\Import\Quickbooks;
|
||||||
|
|
||||||
use App\Services\Import\Quickbooks\Contracts\SdkInterface as QuickbooksInterface;
|
use Carbon\Carbon;
|
||||||
|
use App\Models\Company;
|
||||||
|
use QuickBooksOnline\API\DataService\DataService;
|
||||||
|
use QuickBooksOnline\API\Core\OAuth\OAuth2\OAuth2AccessToken;
|
||||||
|
|
||||||
final class SdkWrapper implements QuickbooksInterface
|
class SdkWrapper
|
||||||
{
|
{
|
||||||
public const MAXRESULTS = 10000;
|
public const MAXRESULTS = 10000;
|
||||||
|
|
||||||
private $sdk;
|
|
||||||
private $entities = ['Customer','Invoice','Payment','Item'];
|
private $entities = ['Customer','Invoice','Payment','Item'];
|
||||||
|
|
||||||
public function __construct($sdk)
|
private OAuth2AccessToken $token;
|
||||||
|
|
||||||
|
public function __construct(public DataService $sdk, private Company $company)
|
||||||
{
|
{
|
||||||
// Prep Data Services
|
$this->init();
|
||||||
$this->sdk = $sdk;
|
}
|
||||||
|
|
||||||
|
private function init(): self
|
||||||
|
{
|
||||||
|
|
||||||
|
isset($this->company->quickbooks->accessTokenKey) ? $this->setNinjaAccessToken($this->company->quickbooks) : null;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAuthorizationUrl(): string
|
public function getAuthorizationUrl(): string
|
||||||
{
|
{
|
||||||
return ($this->sdk->getOAuth2LoginHelper())->getAuthorizationCodeURL();
|
return $this->sdk->getOAuth2LoginHelper()->getAuthorizationCodeURL();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getState(): string
|
public function getState(): string
|
||||||
{
|
{
|
||||||
return ($this->sdk->getOAuth2LoginHelper())->getState();
|
return $this->sdk->getOAuth2LoginHelper()->getState();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAccessToken()
|
public function getRefreshToken(): string
|
||||||
{
|
{
|
||||||
return $this->getTokens();
|
return $this->accessToken()->getRefreshToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRefreshToken(): array
|
public function company()
|
||||||
{
|
{
|
||||||
return $this->getTokens();
|
nlog("getting company info");
|
||||||
}
|
// nlog($this->sdk->getAccessToken());
|
||||||
|
|
||||||
public function accessToken(string $code, string $realm): array
|
return $this->sdk->getCompanyInfo();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
accessTokenKey
|
||||||
|
tokenType
|
||||||
|
refresh_token
|
||||||
|
accessTokenExpiresAt
|
||||||
|
refreshTokenExpiresAt
|
||||||
|
accessTokenValidationPeriod
|
||||||
|
refreshTokenValidationPeriod
|
||||||
|
clientID
|
||||||
|
clientSecret
|
||||||
|
realmID
|
||||||
|
baseURL
|
||||||
|
*/
|
||||||
|
public function accessTokenFromCode(string $code, string $realm): OAuth2AccessToken
|
||||||
{
|
{
|
||||||
$token = ($this->sdk->getOAuth2LoginHelper())->exchangeAuthorizationCodeForToken($code, $realm);
|
$token = $this->sdk->getOAuth2LoginHelper()->exchangeAuthorizationCodeForToken($code, $realm);
|
||||||
|
|
||||||
return $this->getTokens();
|
$this->setAccessToken($token);
|
||||||
|
|
||||||
|
return $this->accessToken();
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getTokens()
|
/**
|
||||||
|
* Set Stored NinjaAccessToken
|
||||||
|
*
|
||||||
|
* @param mixed $token_object
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setNinjaAccessToken(mixed $token_object): self
|
||||||
{
|
{
|
||||||
|
$token = new OAuth2AccessToken(
|
||||||
|
config('services.quickbooks.client_id'),
|
||||||
|
config('services.quickbooks.client_secret'),
|
||||||
|
$token_object->accessTokenKey,
|
||||||
|
$token_object->refresh_token,
|
||||||
|
3600,
|
||||||
|
8726400
|
||||||
|
);
|
||||||
|
|
||||||
$token = ($this->sdk->getOAuth2LoginHelper())->getAccessToken();
|
$token->setAccessTokenExpiresAt($token_object->accessTokenExpiresAt);
|
||||||
return $token;
|
$token->setRefreshTokenExpiresAt($token_object->refreshTokenExpiresAt);
|
||||||
|
$token->setAccessTokenValidationPeriodInSeconds(3600);
|
||||||
|
$token->setRefreshTokenValidationPeriodInSeconds(8726400);
|
||||||
|
|
||||||
// $access_token = $token->getAccessToken();
|
$this->setAccessToken($token);
|
||||||
// $refresh_token = $token->getRefreshToken();
|
|
||||||
// $access_token_expires = $token->getAccessTokenExpiresAt();
|
|
||||||
// $refresh_token_expires = $token->getRefreshTokenExpiresAt();
|
|
||||||
|
|
||||||
// return compact('access_token', 'refresh_token','access_token_expires', 'refresh_token_expires');
|
if($token_object->accessTokenExpiresAt < time()){
|
||||||
|
$new_token = $this->sdk->getOAuth2LoginHelper()->refreshToken();
|
||||||
|
|
||||||
|
$this->setAccessToken($new_token);
|
||||||
|
$this->saveOAuthToken($this->accessToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function refreshToken(): array
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SetsAccessToken
|
||||||
|
*
|
||||||
|
* @param OAuth2AccessToken $token
|
||||||
|
* @return self
|
||||||
|
*/
|
||||||
|
public function setAccessToken(OAuth2AccessToken $token): self
|
||||||
{
|
{
|
||||||
$token = ($this->sdk->getOAuth2LoginHelper())->refreshToken();
|
// $this->sdk = $this->sdk->updateOAuth2Token($token);
|
||||||
$this->sdk = $this->sdk->updateOAuth2Token($token);
|
|
||||||
|
|
||||||
return $this->getTokens();
|
$this->token = $token;
|
||||||
|
|
||||||
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleCallbacks(array $data): void
|
public function accessToken(): OAuth2AccessToken
|
||||||
{
|
{
|
||||||
|
return $this->token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function saveOAuthToken(OAuth2AccessToken $token): void
|
||||||
|
{
|
||||||
|
$obj = new \stdClass();
|
||||||
|
$obj->accessTokenKey = $token->getAccessToken();
|
||||||
|
$obj->refresh_token = $token->getRefreshToken();
|
||||||
|
$obj->accessTokenExpiresAt = Carbon::createFromFormat('Y/m/d H:i:s', $token->getAccessTokenExpiresAt())->timestamp; //@phpstan-ignore-line - QB phpdoc wrong types!!
|
||||||
|
$obj->refreshTokenExpiresAt = Carbon::createFromFormat('Y/m/d H:i:s', $token->getRefreshTokenExpiresAt())->timestamp; //@phpstan-ignore-line - QB phpdoc wrong types!!
|
||||||
|
|
||||||
|
$obj->realmID = $token->getRealmID();
|
||||||
|
$obj->baseURL = $token->getBaseURL();
|
||||||
|
|
||||||
|
$this->company->quickbooks = $obj;
|
||||||
|
$this->company->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Data Access ///
|
||||||
|
|
||||||
public function totalRecords(string $entity): int
|
public function totalRecords(string $entity): int
|
||||||
{
|
{
|
||||||
return $this->sdk->Query("select count(*) from $entity");
|
return (int)$this->sdk->Query("select count(*) from $entity");
|
||||||
}
|
}
|
||||||
|
|
||||||
private function queryData(string $query, int $start = 1, $limit = 100): array
|
private function queryData(string $query, int $start = 1, $limit = 100): array
|
||||||
|
@ -1,89 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Services\Import\Quickbooks;
|
|
||||||
|
|
||||||
use Illuminate\Support\Collection;
|
|
||||||
use Illuminate\Support\Facades\Cache;
|
|
||||||
use App\Services\Import\Quickbooks\Auth;
|
|
||||||
use App\Repositories\Import\Quickbooks\Contracts\RepositoryInterface;
|
|
||||||
use App\Services\Import\Quickbooks\Contracts\SdkInterface as QuickbooksInterface;
|
|
||||||
|
|
||||||
final class Service
|
|
||||||
{
|
|
||||||
private QuickbooksInterface $sdk;
|
|
||||||
|
|
||||||
public function __construct(QuickbooksInterface $quickbooks)
|
|
||||||
{
|
|
||||||
$this->sdk = $quickbooks;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getOAuth(): Auth
|
|
||||||
{
|
|
||||||
return new Auth($this->sdk);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAccessToken(): array
|
|
||||||
{
|
|
||||||
return $this->getOAuth()->getAccessToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getRefreshToken(): array
|
|
||||||
{
|
|
||||||
// TODO: Check if token is Cached otherwise fetch a new one and Cache token and expire
|
|
||||||
return $this->getAccessToken();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* fetch QuickBooks invoice records
|
|
||||||
* @param int $max The maximum records to fetch. Default 100
|
|
||||||
* @return Illuminate\Support\Collection;
|
|
||||||
*/
|
|
||||||
public function fetchInvoices(int $max = 100): Collection
|
|
||||||
{
|
|
||||||
return $this->fetchRecords('Invoice', $max) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetch QuickBooks payment records
|
|
||||||
* @param int $max The maximum records to fetch. Default 100
|
|
||||||
* @return Illuminate\Support\Collection;
|
|
||||||
*/
|
|
||||||
public function fetchPayments(int $max = 100): Collection
|
|
||||||
{
|
|
||||||
return $this->fetchRecords('Payment', $max) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetch QuickBooks product records
|
|
||||||
* @param int $max The maximum records to fetch. Default 100
|
|
||||||
* @return Illuminate\Support\Collection;
|
|
||||||
*/
|
|
||||||
public function fetchItems(int $max = 100): Collection
|
|
||||||
{
|
|
||||||
return $this->fetchRecords('Item', $max) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function fetchRecords(string $entity, $max = 100): Collection
|
|
||||||
{
|
|
||||||
return (self::RepositoryFactory($entity))->get($max);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static function RepositoryFactory(string $entity): RepositoryInterface
|
|
||||||
{
|
|
||||||
return app("\\App\\Repositories\\Import\Quickbooks\\{$entity}Repository");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* fetch QuickBooks customer records
|
|
||||||
* @param int $max The maximum records to fetch. Default 100
|
|
||||||
* @return Illuminate\Support\Collection;
|
|
||||||
*/
|
|
||||||
public function fetchCustomers(int $max = 100): Collection
|
|
||||||
{
|
|
||||||
return $this->fetchRecords('Customer', $max) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function totalRecords(string $entity): int
|
|
||||||
{
|
|
||||||
return (self::RepositoryFactory($entity))->count();
|
|
||||||
}
|
|
||||||
}
|
|
@ -5319,6 +5319,8 @@ $lang = array(
|
|||||||
'one_page_checkout' => 'One-Page Checkout',
|
'one_page_checkout' => 'One-Page Checkout',
|
||||||
'one_page_checkout_help' => 'Enable the new single page payment flow',
|
'one_page_checkout_help' => 'Enable the new single page payment flow',
|
||||||
'applies_to' => 'Applies To',
|
'applies_to' => 'Applies To',
|
||||||
|
'accept_purchase_order' => 'Accept Purchase Order',
|
||||||
|
'round_to_seconds' => 'Round To Seconds',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $lang;
|
return $lang;
|
||||||
|
@ -4847,7 +4847,7 @@ E-mail: :email<b><br><b>',
|
|||||||
'failed' => 'Mislukt',
|
'failed' => 'Mislukt',
|
||||||
'client_contacts' => 'Klantencontacten',
|
'client_contacts' => 'Klantencontacten',
|
||||||
'sync_from' => 'Synchroniseren van',
|
'sync_from' => 'Synchroniseren van',
|
||||||
'gateway_payment_text' => 'Facturen: :invoices voor :amount voor opdrachtgever :client',
|
'gateway_payment_text' => 'Factuur/facturen: :invoices voor :amount voor opdrachtgever :client',
|
||||||
'gateway_payment_text_no_invoice' => 'Betaling zonder factuur voor bedrag :amount voor opdrachtgever :client',
|
'gateway_payment_text_no_invoice' => 'Betaling zonder factuur voor bedrag :amount voor opdrachtgever :client',
|
||||||
'click_to_variables' => 'Klik hier om alle variabelen te zien.',
|
'click_to_variables' => 'Klik hier om alle variabelen te zien.',
|
||||||
'ship_to' => 'Verzend naar',
|
'ship_to' => 'Verzend naar',
|
||||||
@ -5314,6 +5314,11 @@ E-mail: :email<b><br><b>',
|
|||||||
'comments_only' => 'Alleen opmerkingen',
|
'comments_only' => 'Alleen opmerkingen',
|
||||||
'payment_balance_on_file' => 'Huidig saldo',
|
'payment_balance_on_file' => 'Huidig saldo',
|
||||||
'ubl_email_attachment_help' => 'Voor meer e-factuurinstellingen, navigeer naar :here',
|
'ubl_email_attachment_help' => 'Voor meer e-factuurinstellingen, navigeer naar :here',
|
||||||
|
'stop_task_to_add_task_entry' => 'U moet de taak stoppen alvorens een nieuw item toe te voegen.',
|
||||||
|
'xml_file' => 'XML-bestand',
|
||||||
|
'one_page_checkout' => 'Afrekenen op één pagina',
|
||||||
|
'one_page_checkout_help' => 'Schakel de nieuwe betaalflow \'afrekenen op één pagina\' in',
|
||||||
|
'applies_to' => 'Is van toepassing op',
|
||||||
);
|
);
|
||||||
|
|
||||||
return $lang;
|
return $lang;
|
||||||
|
@ -6,7 +6,6 @@ use App\Http\Controllers\Auth\ResetPasswordController;
|
|||||||
use App\Http\Controllers\Bank\NordigenController;
|
use App\Http\Controllers\Bank\NordigenController;
|
||||||
use App\Http\Controllers\Bank\YodleeController;
|
use App\Http\Controllers\Bank\YodleeController;
|
||||||
use App\Http\Controllers\BaseController;
|
use App\Http\Controllers\BaseController;
|
||||||
use App\Http\Controllers\ImportQuickbooksController;
|
|
||||||
use App\Http\Controllers\ClientPortal\ApplePayDomainController;
|
use App\Http\Controllers\ClientPortal\ApplePayDomainController;
|
||||||
use App\Http\Controllers\Gateways\Checkout3dsController;
|
use App\Http\Controllers\Gateways\Checkout3dsController;
|
||||||
use App\Http\Controllers\Gateways\GoCardlessController;
|
use App\Http\Controllers\Gateways\GoCardlessController;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user