mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Quickbooks settings
This commit is contained in:
parent
7745d6db17
commit
b43c9ee59b
36
app/DataMapper/QuickbooksSettings.php
Normal file
36
app/DataMapper/QuickbooksSettings.php
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
<?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\DataMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* QuickbooksSettings.
|
||||||
|
*/
|
||||||
|
class QuickbooksSettings
|
||||||
|
{
|
||||||
|
public string $accessTokenKey;
|
||||||
|
|
||||||
|
public string $refresh_token;
|
||||||
|
|
||||||
|
public string $realmID;
|
||||||
|
|
||||||
|
public int $accessTokenExpiresAt;
|
||||||
|
|
||||||
|
public int $refreshTokenExpiresAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* entity client,invoice,quote,purchase_order,vendor,payment
|
||||||
|
* sync true/false
|
||||||
|
* update_record true/false
|
||||||
|
* direction push/pull/birection
|
||||||
|
* */
|
||||||
|
public array $settings = [];
|
||||||
|
}
|
@ -118,7 +118,7 @@ use Laracasts\Presenter\PresentableTrait;
|
|||||||
* @property string|null $smtp_port
|
* @property string|null $smtp_port
|
||||||
* @property string|null $smtp_encryption
|
* @property string|null $smtp_encryption
|
||||||
* @property string|null $smtp_local_domain
|
* @property string|null $smtp_local_domain
|
||||||
* @property object|null $quickbooks
|
* @property \App\DataMapper\QuickbooksSettings|null $quickbooks
|
||||||
* @property boolean $smtp_verify_peer
|
* @property boolean $smtp_verify_peer
|
||||||
* @property-read \App\Models\Account $account
|
* @property-read \App\Models\Account $account
|
||||||
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
|
* @property-read \Illuminate\Database\Eloquent\Collection<int, \App\Models\Activity> $activities
|
||||||
|
@ -11,9 +11,12 @@
|
|||||||
|
|
||||||
namespace App\Services\Import\Quickbooks;
|
namespace App\Services\Import\Quickbooks;
|
||||||
|
|
||||||
|
use App\Factory\ClientFactory;
|
||||||
|
use App\Models\Client;
|
||||||
use App\Models\Company;
|
use App\Models\Company;
|
||||||
use QuickBooksOnline\API\Core\CoreConstants;
|
use QuickBooksOnline\API\Core\CoreConstants;
|
||||||
use QuickBooksOnline\API\DataService\DataService;
|
use QuickBooksOnline\API\DataService\DataService;
|
||||||
|
use App\Services\Import\Quickbooks\Transformers\ClientTransformer;
|
||||||
|
|
||||||
// quickbooks_realm_id
|
// quickbooks_realm_id
|
||||||
// quickbooks_refresh_token
|
// quickbooks_refresh_token
|
||||||
@ -21,12 +24,24 @@ use QuickBooksOnline\API\DataService\DataService;
|
|||||||
class QuickbooksService
|
class QuickbooksService
|
||||||
{
|
{
|
||||||
public DataService $sdk;
|
public DataService $sdk;
|
||||||
|
|
||||||
|
private $entities = [
|
||||||
|
'client' => 'Customer',
|
||||||
|
'invoice' => 'Invoice',
|
||||||
|
'quote' => 'Estimate',
|
||||||
|
'purchase_order' => 'PurchaseOrder',
|
||||||
|
'payment' => 'Payment',
|
||||||
|
'product' => 'Item',
|
||||||
|
];
|
||||||
|
|
||||||
private bool $testMode = true;
|
private bool $testMode = true;
|
||||||
|
|
||||||
|
private array $settings = [];
|
||||||
|
|
||||||
public function __construct(private Company $company)
|
public function __construct(private Company $company)
|
||||||
{
|
{
|
||||||
$this->init();
|
$this->init();
|
||||||
|
$this->settings = $this->company->quickbooks->settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function init(): self
|
private function init(): self
|
||||||
@ -64,14 +79,91 @@ class QuickbooksService
|
|||||||
] : [];
|
] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getSdk(): DataService
|
|
||||||
{
|
|
||||||
return $this->sdk;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sdk(): SdkWrapper
|
public function sdk(): SdkWrapper
|
||||||
{
|
{
|
||||||
return new SdkWrapper($this->sdk, $this->company);
|
return new SdkWrapper($this->sdk, $this->company);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* //@todo - refactor to a job
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function sync()
|
||||||
|
{
|
||||||
|
//syncable_records.
|
||||||
|
|
||||||
|
foreach($this->entities as $entity)
|
||||||
|
{
|
||||||
|
|
||||||
|
$records = $this->sdk()->fetchRecords($entity);
|
||||||
|
|
||||||
|
$this->processEntitySync($entity, $records);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private function processEntitySync(string $entity, $records)
|
||||||
|
{
|
||||||
|
match($entity){
|
||||||
|
'client' => $this->syncQbToNinjaClients($records),
|
||||||
|
// 'vendor' => $this->syncQbToNinjaClients($records),
|
||||||
|
// 'invoice' => $this->syncInvoices($records),
|
||||||
|
// 'quote' => $this->syncInvoices($records),
|
||||||
|
// 'purchase_order' => $this->syncInvoices($records),
|
||||||
|
// 'payment' => $this->syncPayment($records),
|
||||||
|
// 'product' => $this->syncItem($records),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private function syncQbToNinjaClients(array $records)
|
||||||
|
{
|
||||||
|
foreach($records as $record)
|
||||||
|
{
|
||||||
|
$ninja_client_data = new ClientTransformer($record);
|
||||||
|
|
||||||
|
if($client = $this->findClient($ninja_client_data))
|
||||||
|
{
|
||||||
|
$client->fill($ninja_client_data[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function findClient(array $qb_data)
|
||||||
|
{
|
||||||
|
$client = $qb_data[0];
|
||||||
|
$contact = $qb_data[1];
|
||||||
|
$client_meta = $qb_data[2];
|
||||||
|
|
||||||
|
$search = Client::query()
|
||||||
|
->withTrashed()
|
||||||
|
->where('company', $this->company->id)
|
||||||
|
->where(function ($q) use ($client, $client_meta, $contact){
|
||||||
|
|
||||||
|
$q->where('client_hash', $client_meta['client_hash'])
|
||||||
|
->orWhere('id_number', $client['id_number'])
|
||||||
|
->orWhereHas('contacts', function ($q) use ($contact){
|
||||||
|
$q->where('email', $contact['email']);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
if($search->count() == 0) {
|
||||||
|
//new client
|
||||||
|
$client = ClientFactory::create($this->company->id, $this->company->owner()->id);
|
||||||
|
$client->client_hash = $client_meta['client_hash'];
|
||||||
|
$client->settings = $client_meta['settings'];
|
||||||
|
|
||||||
|
return $client;
|
||||||
|
}
|
||||||
|
elseif($search->count() == 1) {
|
||||||
|
// ? sync / update
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//potentially multiple matching clients?
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,12 +154,17 @@ class SdkWrapper
|
|||||||
return (int)$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 = 1000): array
|
||||||
{
|
{
|
||||||
return (array) $this->sdk->Query($query, $start, $limit);
|
return (array) $this->sdk->Query($query, $start, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function fetchRecords(string $entity, int $max = 1000): array
|
public function fetchById(string $entity, $id)
|
||||||
|
{
|
||||||
|
return $this->sdk->FindById($entity, $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchRecords(string $entity, int $max = 100000): array
|
||||||
{
|
{
|
||||||
|
|
||||||
if(!in_array($entity, $this->entities)) {
|
if(!in_array($entity, $this->entities)) {
|
||||||
@ -168,7 +173,7 @@ class SdkWrapper
|
|||||||
|
|
||||||
$records = [];
|
$records = [];
|
||||||
$start = 0;
|
$start = 0;
|
||||||
$limit = 100;
|
$limit = 1000;
|
||||||
try {
|
try {
|
||||||
$total = $this->totalRecords($entity);
|
$total = $this->totalRecords($entity);
|
||||||
$total = min($max, $total);
|
$total = min($max, $total);
|
||||||
|
@ -12,61 +12,70 @@
|
|||||||
|
|
||||||
namespace App\Services\Import\Quickbooks\Transformers;
|
namespace App\Services\Import\Quickbooks\Transformers;
|
||||||
|
|
||||||
|
use App\DataMapper\ClientSettings;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class ClientTransformer.
|
* Class ClientTransformer.
|
||||||
*/
|
*/
|
||||||
class ClientTransformer
|
class ClientTransformer
|
||||||
{
|
{
|
||||||
|
|
||||||
private $fillable = [
|
|
||||||
'name' => 'CompanyName',
|
|
||||||
'phone' => 'PrimaryPhone.FreeFormNumber',
|
|
||||||
'country_id' => 'BillAddr.Country',
|
|
||||||
'state' => 'BillAddr.CountrySubDivisionCode',
|
|
||||||
'address1' => 'BillAddr.Line1',
|
|
||||||
'city' => 'BillAddr.City',
|
|
||||||
'postal_code' => 'BillAddr.PostalCode',
|
|
||||||
'shipping_country_id' => 'ShipAddr.Country',
|
|
||||||
'shipping_state' => 'ShipAddr.CountrySubDivisionCode',
|
|
||||||
'shipping_address1' => 'ShipAddr.Line1',
|
|
||||||
'shipping_city' => 'ShipAddr.City',
|
|
||||||
'shipping_postal_code' => 'ShipAddr.PostalCode',
|
|
||||||
'public_notes' => 'Notes'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __invoke($qb_data)
|
public function __invoke($qb_data): array
|
||||||
{
|
{
|
||||||
return $this->transform($qb_data);
|
return $this->transform($qb_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function transform($data): array
|
||||||
public function transform($data)
|
|
||||||
{
|
{
|
||||||
$transformed_data = [];
|
|
||||||
// Assuming 'customer_name' is equivalent to 'CompanyName'
|
|
||||||
if (isset($data['CompanyName']) && $this->hasClient($data['CompanyName'])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$transformed_data = $this->preTransform($data);
|
$contact = [
|
||||||
$transformed_data['contacts'][0] = $this->getContacts($data)->toArray() + ['company_id' => $this->company->id, 'user_id' => $this->company->owner()->id ];
|
'first_name' => data_get($data, 'GivenName'),
|
||||||
|
'last_name' => data_get($data, 'FamilyName'),
|
||||||
|
'phone' => data_get($data, 'PrimaryPhone.FreeFormNumber'),
|
||||||
|
'email' => data_get($data, 'PrimaryEmailAddr.Address'),
|
||||||
|
];
|
||||||
|
|
||||||
return $transformed_data;
|
$client = [
|
||||||
|
'name' => data_get($data,'CompanyName', ''),
|
||||||
|
'address1' => data_get($data, 'BillAddr.Line1', ''),
|
||||||
|
'address2' => data_get($data, 'BillAddr.Line2', ''),
|
||||||
|
'city' => data_get($data, 'BillAddr.City', ''),
|
||||||
|
'country_id' => $this->resolveCountry(data_get($data, 'BillAddr.Country', '')),
|
||||||
|
'state' => data_get($data, 'BillAddr.CountrySubDivisionCode', ''),
|
||||||
|
'postal_code' => data_get($data, 'BillAddr.PostalCode', ''),
|
||||||
|
'shipping_address1' => data_get($data, 'ShipAddr.Line1', ''),
|
||||||
|
'shipping_address2' => data_get($data, 'ShipAddr.Line2', ''),
|
||||||
|
'shipping_city' => data_get($data, 'ShipAddr.City', ''),
|
||||||
|
'shipping_country_id' => $this->resolveCountry(data_get($data, 'ShipAddr.Country', '')),
|
||||||
|
'shipping_state' => data_get($data, 'ShipAddr.CountrySubDivisionCode', ''),
|
||||||
|
'shipping_postal_code' => data_get($data, 'BillAddr.PostalCode', ''),
|
||||||
|
'id_number' => data_get($data, 'Id', ''),
|
||||||
|
];
|
||||||
|
|
||||||
|
$settings = ClientSettings::defaults();
|
||||||
|
$settings->currency_id = (string) $this->resolveCurrency(data_get($data, 'CurrencyRef.value'));
|
||||||
|
|
||||||
|
$new_client_merge = [
|
||||||
|
'client_hash' => data_get($data, 'V4IDPseudonym', \Illuminate\Support\Str::random(32)),
|
||||||
|
'settings' => $settings,
|
||||||
|
];
|
||||||
|
|
||||||
|
return [$client, $contact, $new_client_merge];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getContacts($data)
|
private function resolveCountry(string $iso_3_code)
|
||||||
{
|
{
|
||||||
return (new ClientContact())->fill([
|
return (string) app('countries')->first(function ($c) use ($iso_3_code){
|
||||||
'first_name' => $this->getString($data, 'GivenName'),
|
return $c->iso_3166_3 == $iso_3_code;
|
||||||
'last_name' => $this->getString($data, 'FamilyName'),
|
})->id ?? 840;
|
||||||
'phone' => $this->getString($data, 'PrimaryPhone.FreeFormNumber'),
|
|
||||||
'email' => $this->getString($data, 'PrimaryEmailAddr.Address'),
|
|
||||||
'company_id' => $this->company->id,
|
|
||||||
'user_id' => $this->company->owner()->id,
|
|
||||||
'send_email' => true,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function resolveCurrency(string $currency_code)
|
||||||
|
{
|
||||||
|
return (string) app('currencies')->first(function($c) use ($currency_code){
|
||||||
|
return $c->code == $currency_code;
|
||||||
|
}) ?? 'USD';
|
||||||
|
}
|
||||||
|
|
||||||
public function getShipAddrCountry($data, $field)
|
public function getShipAddrCountry($data, $field)
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user