mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2026-03-02 22:57:00 +00:00
Transform company
This commit is contained in:
@@ -20,15 +20,23 @@ use App\Services\Quickbooks\QuickbooksService;
|
||||
|
||||
class ImportQuickbooksController extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
* authorizeQuickbooks
|
||||
*
|
||||
* Starts the Quickbooks authorization process.
|
||||
*
|
||||
* @param mixed $request
|
||||
* @param string $token
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function authorizeQuickbooks(AuthQuickbooksRequest $request, string $token)
|
||||
{
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||
|
||||
$company = $request->getCompany();
|
||||
|
||||
$qb = new QuickbooksService($company);
|
||||
|
||||
$authorizationUrl = $qb->sdk()->getAuthorizationUrl();
|
||||
@@ -39,18 +47,45 @@ class ImportQuickbooksController extends BaseController
|
||||
|
||||
return redirect()->to($authorizationUrl);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* onAuthorized
|
||||
*
|
||||
* Handles the callback from Quickbooks after authorization.
|
||||
*
|
||||
* @param AuthorizedQuickbooksRequest $request
|
||||
* @return RedirectResponse
|
||||
*/
|
||||
public function onAuthorized(AuthorizedQuickbooksRequest $request)
|
||||
{
|
||||
|
||||
nlog($request->all());
|
||||
|
||||
MultiDB::findAndSetDbByCompanyKey($request->getTokenContent()['company_key']);
|
||||
$company = $request->getCompany();
|
||||
|
||||
$qb = new QuickbooksService($company);
|
||||
|
||||
$realm = $request->query('realmId');
|
||||
|
||||
nlog($realm);
|
||||
|
||||
$access_token_object = $qb->sdk()->accessTokenFromCode($request->query('code'), $realm);
|
||||
|
||||
nlog($access_token_object);
|
||||
|
||||
$qb->sdk()->saveOAuthToken($access_token_object);
|
||||
|
||||
// Refresh the service to initialize SDK with the new access token
|
||||
$qb->refresh();
|
||||
|
||||
$companyInfo = $qb->sdk()->company();
|
||||
|
||||
$company->quickbooks->companyName = $companyInfo->CompanyName;
|
||||
$company->save();
|
||||
|
||||
nlog($companyInfo);
|
||||
|
||||
return redirect(config('ninja.react_url'));
|
||||
|
||||
}
|
||||
|
||||
@@ -14,9 +14,10 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Libraries\MultiDB;
|
||||
use Illuminate\Http\Response;
|
||||
use App\Services\Quickbooks\QuickbooksService;
|
||||
use App\Http\Requests\Quickbooks\SyncQuickbooksRequest;
|
||||
use App\Http\Requests\Quickbooks\ConfigQuickbooksRequest;
|
||||
use App\Http\Requests\Quickbooks\DisconnectQuickbooksRequest;
|
||||
use App\Http\Requests\Quickbooks\SyncQuickbooksRequest;
|
||||
|
||||
class QuickbooksController extends BaseController
|
||||
{
|
||||
@@ -53,7 +54,9 @@ class QuickbooksController extends BaseController
|
||||
$company = $user->company();
|
||||
|
||||
$qb = new QuickbooksService($company);
|
||||
$qb->sdk()->revokeAccessToken();
|
||||
$rs = $qb->sdk()->revokeAccessToken();
|
||||
|
||||
nlog($rs);
|
||||
|
||||
$company->quickbooks = null;
|
||||
$company->save();
|
||||
|
||||
@@ -69,7 +69,7 @@ class QuickbooksService
|
||||
'ClientSecret' => config('services.quickbooks.client_secret'),
|
||||
'auth_mode' => 'oauth2',
|
||||
'scope' => "com.intuit.quickbooks.accounting",
|
||||
'RedirectURI' => $this->testMode ? 'https://grok.romulus.com.au/quickbooks/authorized' : 'https://invoicing.co/quickbooks/authorized',
|
||||
'RedirectURI' => $this->testMode ? 'https://qb.romulus.com.au/quickbooks/authorized' : 'https://invoicing.co/quickbooks/authorized',
|
||||
'baseUrl' => $this->testMode ? CoreConstants::SANDBOX_DEVELOPMENT : CoreConstants::QBO_BASEURL,
|
||||
];
|
||||
|
||||
@@ -134,6 +134,24 @@ class QuickbooksService
|
||||
// return $this;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Refresh the service after OAuth token has been updated.
|
||||
* This reloads the company from the database and reinitializes the SDK
|
||||
* with the new access token.
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function refresh(): self
|
||||
{
|
||||
// Reload company from database to get fresh token data
|
||||
$this->company = $this->company->fresh();
|
||||
|
||||
// Reinitialize the SDK with the updated token
|
||||
$this->init();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function checkToken(): self
|
||||
{
|
||||
|
||||
|
||||
@@ -55,6 +55,11 @@ class SdkWrapper
|
||||
return $this->accessToken()->getRefreshToken();
|
||||
}
|
||||
|
||||
public function revokeAccessToken()
|
||||
{
|
||||
return $this->sdk->getOAuth2LoginHelper()->revokeToken($this->accessToken()->getAccessToken());
|
||||
}
|
||||
|
||||
public function company()
|
||||
{
|
||||
return $this->sdk->getCompanyInfo();
|
||||
|
||||
@@ -50,6 +50,21 @@ class BaseTransformer
|
||||
return $currency ? (string) $currency->id : $this->company->settings->currency_id;
|
||||
}
|
||||
|
||||
public function resolveTimezone(?string $timezone_name): string
|
||||
{
|
||||
if (empty($timezone_name)) {
|
||||
return (string) $this->company->settings->timezone_id;
|
||||
}
|
||||
|
||||
/** @var \App\Models\Timezone $timezone */
|
||||
$timezone = app('timezones')->first(function ($t) use ($timezone_name) {
|
||||
/** @var \App\Models\Timezone $t */
|
||||
return $t->name === $timezone_name;
|
||||
});
|
||||
|
||||
return $timezone ? (string) $timezone->id : (string) $this->company->settings->timezone_id;
|
||||
}
|
||||
|
||||
public function getShipAddrCountry($data, $field)
|
||||
{
|
||||
return is_null(($c = $this->getString($data, $field))) ? null : $this->getCountryId($c);
|
||||
|
||||
99
app/Services/Quickbooks/Transformers/CompanyTransformer.php
Normal file
99
app/Services/Quickbooks/Transformers/CompanyTransformer.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Invoice Ninja (https://invoiceninja.com).
|
||||
*
|
||||
* @link https://github.com/invoiceninja/invoiceninja source repository
|
||||
*
|
||||
* @copyright Copyright (c) 2025. Invoice Ninja LLC (https://invoiceninja.com)
|
||||
*
|
||||
* @license https://www.elastic.co/licensing/elastic-license
|
||||
*/
|
||||
|
||||
namespace App\Services\Quickbooks\Transformers;
|
||||
|
||||
/**
|
||||
* Transforms QuickBooks IPPCompanyInfo into Invoice Ninja company data.
|
||||
*
|
||||
* QB fields: CompanyName, LegalName, CompanyAddr, LegalAddr, CustomerCommunicationAddr,
|
||||
* Email, CustomerCommunicationEmailAddr, PrimaryPhone, WebAddr, CompanyURL,
|
||||
* Country, DefaultTimeZone.
|
||||
*/
|
||||
class CompanyTransformer extends BaseTransformer
|
||||
{
|
||||
/**
|
||||
* Transform QuickBooks company info to Ninja structure.
|
||||
*
|
||||
* @param mixed $qb_data QuickBooksOnline\API\Data\IPPCompanyInfo (or array)
|
||||
* @return array{quickbooks: array, settings: array}
|
||||
*/
|
||||
public function qbToNinja(mixed $qb_data): array
|
||||
{
|
||||
return $this->transform($qb_data);
|
||||
}
|
||||
|
||||
public function ninjaToQb(): void
|
||||
{
|
||||
// Reserved for Ninja → QB sync when needed.
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $data IPPCompanyInfo object or array
|
||||
* @return array{quickbooks: array<string, mixed>, settings: array<string, mixed>}
|
||||
*/
|
||||
public function transform(mixed $data): array
|
||||
{
|
||||
$addr = $this->pickAddress($data);
|
||||
$country_raw = data_get($addr, 'Country') ?? data_get($addr, 'CountryCode') ?? data_get($data, 'Country');
|
||||
$country_id = $this->resolveCountry($country_raw);
|
||||
|
||||
$quickbooks = [
|
||||
'companyName' => data_get($data, 'CompanyName', '') ?: data_get($data, 'LegalName', ''),
|
||||
];
|
||||
|
||||
$settings = [
|
||||
'address1' => data_get($addr, 'Line1', ''),
|
||||
'address2' => data_get($addr, 'Line2', ''),
|
||||
'city' => data_get($addr, 'City', ''),
|
||||
'state' => data_get($addr, 'CountrySubDivisionCode', ''),
|
||||
'postal_code' => data_get($addr, 'PostalCode', ''),
|
||||
'country_id' => $country_id,
|
||||
'phone' => $this->pickPhone($data),
|
||||
'email' => $this->pickEmail($data),
|
||||
'website' => data_get($data, 'WebAddr', '') ?: data_get($data, 'CompanyURL', ''),
|
||||
'timezone_id' => $this->resolveTimezone(data_get($data, 'DefaultTimeZone')),
|
||||
];
|
||||
|
||||
return [
|
||||
'quickbooks' => $quickbooks,
|
||||
'settings' => $settings,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer CompanyAddr, then LegalAddr, then CustomerCommunicationAddr.
|
||||
*
|
||||
* @param mixed $data
|
||||
* @return object|array|null
|
||||
*/
|
||||
private function pickAddress(mixed $data)
|
||||
{
|
||||
$addr = data_get($data, 'CompanyAddr') ?? data_get($data, 'LegalAddr') ?? data_get($data, 'CustomerCommunicationAddr');
|
||||
|
||||
return is_object($addr) ? $addr : (is_array($addr) ? $addr : []);
|
||||
}
|
||||
|
||||
private function pickPhone(mixed $data): string
|
||||
{
|
||||
$phone = data_get($data, 'PrimaryPhone.FreeFormNumber');
|
||||
|
||||
return is_string($phone) ? $phone : '';
|
||||
}
|
||||
|
||||
private function pickEmail(mixed $data): string
|
||||
{
|
||||
$email = data_get($data, 'Email.Address') ?? data_get($data, 'CustomerCommunicationEmailAddr.Address') ?? data_get($data, 'CompanyEmailAddr');
|
||||
|
||||
return is_string($email) ? $email : '';
|
||||
}
|
||||
}
|
||||
@@ -152,6 +152,7 @@ return [
|
||||
'quickbooks_webhook' => [
|
||||
'verifier_token' => env('QUICKBOOKS_VERIFIER_TOKEN', false),
|
||||
],
|
||||
|
||||
'verifactu' => [
|
||||
'sender_nif' => env('VERIFACTU_SENDER_NIF', ''),
|
||||
'certificate' => env('VERIFACTU_CERTIFICATE', ''),
|
||||
@@ -159,6 +160,14 @@ return [
|
||||
'sender_name' => env('VERIFACTU_SENDER_NAME', 'CERTIFICADO FISICA PRUEBAS'),
|
||||
'test_mode' => env('VERIFACTU_TEST_MODE', false),
|
||||
],
|
||||
'quickbooks' => [
|
||||
'client_id' => env('QUICKBOOKS_CLIENT_ID', false),
|
||||
'client_secret' => env('QUICKBOOKS_CLIENT_SECRET', false),
|
||||
'redirect' => env('QUICKBOOKS_REDIRECT_URI'),
|
||||
'test_redirect' => env('QUICKBOOKS_TEST_REDIRECT_URI'),
|
||||
'env' => env('QUICKBOOKS_ENV', 'sandbox'),
|
||||
'debug' => env('APP_DEBUG',false)
|
||||
],
|
||||
'cloudflare' => [
|
||||
'zone_id' => env('CLOUDFLARE_SAAS_ZONE_ID', false),
|
||||
'api_token' => env('CLOUDFLARE_SAAS_API_TOKEN', false),
|
||||
|
||||
@@ -61,6 +61,7 @@ use App\Http\Controllers\SystemLogController;
|
||||
use App\Http\Controllers\TwoFactorController;
|
||||
use App\Http\Controllers\Auth\LoginController;
|
||||
use App\Http\Controllers\ImportJsonController;
|
||||
use App\Http\Controllers\QuickbooksController;
|
||||
use App\Http\Controllers\SelfUpdateController;
|
||||
use App\Http\Controllers\TaskStatusController;
|
||||
use App\Http\Controllers\Bank\YodleeController;
|
||||
@@ -336,8 +337,9 @@ Route::group(['middleware' => ['throttle:api', 'token_auth', 'valid_json','local
|
||||
Route::get('quote/{invitation_key}/download', [QuoteController::class, 'downloadPdf'])->name('quotes.downloadPdf');
|
||||
Route::get('quote/{invitation_key}/download_e_quote', [QuoteController::class, 'downloadEQuote'])->name('quotes.downloadEQuote');
|
||||
|
||||
Route::post('quickbooks/sync', [ImportQuickbooksController::class, 'sync'])->name('quickbooks.sync');
|
||||
Route::post('quickbooks/configuration', [ImportQuickbooksController::class, 'configuration'])->name('quickbooks.configuration');
|
||||
Route::post('quickbooks/sync', [QuickbooksController::class, 'sync'])->name('quickbooks.sync');
|
||||
Route::post('quickbooks/configuration', [QuickbooksController::class, 'configuration'])->name('quickbooks.configuration');
|
||||
Route::post('quickbooks/disconnect', [QuickbooksController::class, 'disconnect'])->name('quickbooks.disconnect');
|
||||
|
||||
Route::resource('recurring_expenses', RecurringExpenseController::class);
|
||||
Route::post('recurring_expenses/bulk', [RecurringExpenseController::class, 'bulk'])->name('recurring_expenses.bulk');
|
||||
|
||||
Reference in New Issue
Block a user