diff --git a/app/Services/Quickbooks/Jobs/QuickbooksImport.php b/app/Services/Quickbooks/Jobs/QuickbooksImport.php index 842c7ff34233..9e99d1c560e1 100644 --- a/app/Services/Quickbooks/Jobs/QuickbooksImport.php +++ b/app/Services/Quickbooks/Jobs/QuickbooksImport.php @@ -84,8 +84,6 @@ class QuickbooksImport implements ShouldQueue foreach($this->entities as $key => $entity) { - nlog($key); - if(!$this->syncGate($key, 'pull')) { nlog('skipping ' . $key); continue; @@ -132,7 +130,7 @@ class QuickbooksImport implements ShouldQueue private function processEntitySync(string $entity, $records): void { match($entity){ - 'client' => $this->syncQbToNinjaClients($records), + 'client' => $this->qbs->client->syncToNinja($records), 'product' => $this->qbs->product->syncToNinja($records), // 'invoice' => $this->syncQbToNinjaInvoices($records), // 'sales' => $this->syncQbToNinjaInvoices($records), @@ -233,40 +231,40 @@ class QuickbooksImport implements ShouldQueue } - private function syncQbToNinjaClients(array $records): void - { + // private function syncQbToNinjaClients(array $records): void + // { - $client_transformer = new ClientTransformer($this->company); + // $client_transformer = new ClientTransformer($this->company); - foreach($records as $record) - { - $ninja_client_data = $client_transformer->qbToNinja($record); + // foreach($records as $record) + // { + // $ninja_client_data = $client_transformer->qbToNinja($record); - if($client = $this->findClient($ninja_client_data)) - { - $client->fill($ninja_client_data[0]); - $client->saveQuietly(); + // if($client = $this->findClient($ninja_client_data)) + // { + // $client->fill($ninja_client_data[0]); + // $client->saveQuietly(); - $contact = $client->contacts()->where('email', $ninja_client_data[1]['email'])->first(); + // $contact = $client->contacts()->where('email', $ninja_client_data[1]['email'])->first(); - if(!$contact) - { - $contact = ClientContactFactory::create($this->company->id, $this->company->owner()->id); - $contact->client_id = $client->id; - $contact->send_email = true; - $contact->is_primary = true; - $contact->fill($ninja_client_data[1]); - $contact->saveQuietly(); - } - elseif($this->updateGate('client')){ - $contact->fill($ninja_client_data[1]); - $contact->saveQuietly(); - } + // if(!$contact) + // { + // $contact = ClientContactFactory::create($this->company->id, $this->company->owner()->id); + // $contact->client_id = $client->id; + // $contact->send_email = true; + // $contact->is_primary = true; + // $contact->fill($ninja_client_data[1]); + // $contact->saveQuietly(); + // } + // elseif($this->updateGate('client')){ + // $contact->fill($ninja_client_data[1]); + // $contact->saveQuietly(); + // } - } + // } - } - } + // } + // } private function syncQbToNinjaVendors(array $records): void { @@ -321,23 +319,6 @@ class QuickbooksImport implements ShouldQueue } } - - private function syncQbToNinjaProducts($records): void - { - $product_transformer = new ProductTransformer($this->company); - - foreach($records as $record) - { - $ninja_data = $product_transformer->qbToNinja($record); - - if($product = $this->findProduct($ninja_data['hash'])) - { - $product->fill($ninja_data); - $product->save(); - } - } - } - private function findExpense(array $qb_data): ?Expense { $expense = $qb_data; diff --git a/app/Services/Quickbooks/Models/QbClient.php b/app/Services/Quickbooks/Models/QbClient.php index 1528aca44064..97484aa51744 100644 --- a/app/Services/Quickbooks/Models/QbClient.php +++ b/app/Services/Quickbooks/Models/QbClient.php @@ -11,12 +11,13 @@ namespace App\Services\Quickbooks\Models; -use App\DataMapper\ClientSync; -use App\Services\Quickbooks\QuickbooksService; use App\Models\Client; +use App\DataMapper\ClientSync; use App\Factory\ClientFactory; -use App\Services\Quickbooks\Transformers\ClientTransformer; use App\Interfaces\SyncInterface; +use App\Factory\ClientContactFactory; +use App\Services\Quickbooks\QuickbooksService; +use App\Services\Quickbooks\Transformers\ClientTransformer; class QbClient implements SyncInterface { @@ -38,9 +39,41 @@ class QbClient implements SyncInterface $ninja_data = $transformer->qbToNinja($record); - if ($client = $this->findClient($ninja_data['id'])) { - $client->fill($ninja_data); + if($ninja_data[0]['terms']){ + + $days = $this->service->findEntityById('Term', $ninja_data[0]['terms']); + + nlog($days); + + if($days){ + $ninja_data[0]['settings']->payment_terms = (string)$days->DueDays; + } + + } + + if ($client = $this->findClient($ninja_data[0]['id'])) { + + $qbc = $this->find($ninja_data[0]['id']); + + $client->fill($ninja_data[0]); $client->service()->applyNumber()->save(); + + $contact = $client->contacts()->where('email', $ninja_data[1]['email'])->first(); + + if(!$contact) + { + $contact = ClientContactFactory::create($this->service->company->id, $this->service->company->owner()->id); + $contact->client_id = $client->id; + $contact->send_email = true; + $contact->is_primary = true; + $contact->fill($ninja_data[1]); + $contact->saveQuietly(); + } + elseif($this->updateGate('client')){ + $contact->fill($ninja_data[1]); + $contact->saveQuietly(); + } + } } @@ -50,6 +83,11 @@ class QbClient implements SyncInterface { } + private function updateGate(string $entity): bool + { + return (bool) $this->service->settings->{$entity}->sync && $this->service->settings->{$entity}->update_record; + } + private function findClient(string $key): ?Client { $search = Client::query() diff --git a/app/Services/Quickbooks/Models/QbProduct.php b/app/Services/Quickbooks/Models/QbProduct.php index 4c5ca8d2b835..d7c05c844630 100644 --- a/app/Services/Quickbooks/Models/QbProduct.php +++ b/app/Services/Quickbooks/Models/QbProduct.php @@ -11,18 +11,24 @@ namespace App\Services\Quickbooks\Models; -use App\DataMapper\ProductSync; -use App\Services\Quickbooks\QuickbooksService; +use Carbon\Carbon; use App\Models\Product; +use App\DataMapper\ProductSync; use App\Factory\ProductFactory; -use App\Services\Quickbooks\Transformers\ProductTransformer; use App\Interfaces\SyncInterface; +use App\Services\Quickbooks\QuickbooksService; +use App\Services\Quickbooks\Transformers\ProductTransformer; class QbProduct implements SyncInterface { + protected ProductTransformer $product_transformer; + public function __construct(public QuickbooksService $service) { + + $this->product_transformer = new ProductTransformer($service->company); + } public function find(string $id): mixed @@ -33,11 +39,9 @@ class QbProduct implements SyncInterface public function syncToNinja(array $records): void { - $product_transformer = new ProductTransformer($this->service->company); - foreach ($records as $record) { - $ninja_data = $product_transformer->qbToNinja($record); + $ninja_data = $this->product_transformer->qbToNinja($record); if ($product = $this->findProduct($ninja_data['id'])) { $product->fill($ninja_data); @@ -74,6 +78,25 @@ class QbProduct implements SyncInterface return null; + } + + public function sync(string $id): void + { + $qb_record = $this->find($id); + + if($ninja_record = $this->findProduct($id)) + { + + if(Carbon::parse($qb_record->lastUpdated) > Carbon::parse($ninja_record->updated_at)) + { + $transformed_qb_product = $this->product_transformer($qb_record); + + $ninja_record->fill($ninja_data); + $ninja_record->save(); + + } + + } } } diff --git a/app/Services/Quickbooks/QuickbooksService.php b/app/Services/Quickbooks/QuickbooksService.php index 3083883e17eb..d2f0d691d922 100644 --- a/app/Services/Quickbooks/QuickbooksService.php +++ b/app/Services/Quickbooks/QuickbooksService.php @@ -25,6 +25,7 @@ use App\Services\Quickbooks\Models\QbInvoice; use App\Services\Quickbooks\Models\QbProduct; use QuickBooksOnline\API\DataService\DataService; use App\Services\Quickbooks\Jobs\QuickbooksImport; +use App\Services\Quickbooks\Models\QbClient; use App\Services\Quickbooks\Transformers\ClientTransformer; use App\Services\Quickbooks\Transformers\InvoiceTransformer; use App\Services\Quickbooks\Transformers\PaymentTransformer; @@ -38,10 +39,14 @@ class QuickbooksService public QbProduct $product; + public QbClient $client; + public QuickbooksSync $settings; private bool $testMode = true; + private bool $try_refresh = true; + public function __construct(public Company $company) { $this->init(); @@ -70,15 +75,39 @@ class QuickbooksService $this->sdk->setMinorVersion("73"); $this->sdk->throwExceptionOnError(true); + $this->checkToken(); + $this->invoice = new QbInvoice($this); $this->product = new QbProduct($this); + $this->client = new QbClient($this); + $this->settings = $this->company->quickbooks->settings; return $this; } + private function checkToken(): self + { + + if($this->company->quickbooks->accessTokenKey > time()) + return $this; + + if($this->company->quickbooks->accessTokenExpiresAt < time() && $this->try_refresh){ + $this->sdk()->refreshToken($this->company->quickbooks->refresh_token); + $this->company = $this->company->fresh(); + $this->try_refresh = false; + $this->init(); + + return $this; + } + + nlog('Quickbooks token expired and could not be refreshed => ' .$this->company->company_key); + throw new \Exception('Quickbooks token expired and could not be refreshed'); + + } + private function ninjaAccessToken(): array { return isset($this->company->quickbooks->accessTokenKey) ? [ @@ -103,4 +132,8 @@ class QuickbooksService QuickbooksImport::dispatch($this->company->id, $this->company->db); } + public function findEntityById(string $entity, string $id): mixed + { + return $this->sdk->FindById($entity, $id); + } } diff --git a/app/Services/Quickbooks/SdkWrapper.php b/app/Services/Quickbooks/SdkWrapper.php index 2845b747ab89..c0ded99c650d 100644 --- a/app/Services/Quickbooks/SdkWrapper.php +++ b/app/Services/Quickbooks/SdkWrapper.php @@ -105,7 +105,7 @@ class SdkWrapper $this->setAccessToken($token); if($token_object->accessTokenExpiresAt < time()){ - $new_token = $this->sdk->getOAuth2LoginHelper()->refreshToken(); + $new_token = $this->sdk->getOAuth2LoginHelper()->refreshAccessTokenWithRefreshToken($token_object->refresh_token); $this->setAccessToken($new_token); $this->saveOAuthToken($this->accessToken()); @@ -114,6 +114,18 @@ class SdkWrapper return $this; } + + public function refreshToken(string $refresh_token): self + { + $new_token = $this->sdk->getOAuth2LoginHelper()->refreshAccessTokenWithRefreshToken($refresh_token); + + nlog($new_token); + $this->setAccessToken($new_token); + $this->saveOAuthToken($this->accessToken()); + + return $this; + } + /** * SetsAccessToken * diff --git a/app/Services/Quickbooks/Transformers/ClientTransformer.php b/app/Services/Quickbooks/Transformers/ClientTransformer.php index 69b0566ded99..6a320cb70f91 100644 --- a/app/Services/Quickbooks/Transformers/ClientTransformer.php +++ b/app/Services/Quickbooks/Transformers/ClientTransformer.php @@ -31,6 +31,7 @@ class ClientTransformer extends BaseTransformer public function transform(mixed $data): array { + nlog($data); $contact = [ 'first_name' => data_get($data, 'GivenName'), @@ -54,16 +55,20 @@ class ClientTransformer extends BaseTransformer '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', ''), - 'number' => data_get($data, 'Id.value', ''), + 'client_hash' => data_get($data, 'V4IDPseudonym', \Illuminate\Support\Str::random(32)), + 'vat_number' => data_get($data, 'PrimaryTaxIdentifier', ''), + 'id_number' => data_get($data, 'BusinessNumber', ''), + 'terms' => data_get($data, 'SalesTermRef.value', false), + 'is_tax_exempt' => !data_get($data, 'Taxable', false), + 'private_notes' => data_get($data, 'Notes', ''), ]; $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, - ]; + $client['settings'] = $settings; + + $new_client_merge = []; return [$client, $contact, $new_client_merge]; }