diff --git a/app/Services/Quickbooks/Jobs/QuickbooksImport.php b/app/Services/Quickbooks/Jobs/QuickbooksImport.php index 9901d388ece1..105c5e6ad898 100644 --- a/app/Services/Quickbooks/Jobs/QuickbooksImport.php +++ b/app/Services/Quickbooks/Jobs/QuickbooksImport.php @@ -53,10 +53,10 @@ class QuickbooksImport implements ShouldQueue 'product' => 'Item', 'client' => 'Customer', 'invoice' => 'Invoice', + 'sales' => 'SalesReceipt', // 'quote' => 'Estimate', // 'purchase_order' => 'PurchaseOrder', // 'payment' => 'Payment', - 'sales' => 'SalesReceipt', // 'vendor' => 'Vendor', // 'expense' => 'Purchase', ]; diff --git a/app/Services/Quickbooks/Models/QbInvoice.php b/app/Services/Quickbooks/Models/QbInvoice.php index 9f3ec1786c07..0cf18aba807a 100644 --- a/app/Services/Quickbooks/Models/QbInvoice.php +++ b/app/Services/Quickbooks/Models/QbInvoice.php @@ -11,33 +11,39 @@ namespace App\Services\Quickbooks\Models; +use Carbon\Carbon; use App\Models\Invoice; +use App\DataMapper\InvoiceSync; use App\Factory\InvoiceFactory; use App\Interfaces\SyncInterface; +use App\Repositories\InvoiceRepository; use App\Services\Quickbooks\QuickbooksService; use App\Services\Quickbooks\Transformers\InvoiceTransformer; use App\Services\Quickbooks\Transformers\PaymentTransformer; class QbInvoice implements SyncInterface { - protected InvoiceTransformer $transformer; + protected InvoiceTransformer $invoice_transformer; + protected InvoiceRepository $invoice_repository; + public function __construct(public QuickbooksService $service) { - $this->transformer = new InvoiceTransformer($this->service->company); + $this->invoice_transformer = new InvoiceTransformer($this->service->company); + $this->invoice_repository = new InvoiceRepository(); } - public function find(int $id): mixed + public function find(string $id): mixed { return $this->service->sdk->FindById('Invoice', $id); } public function syncToNinja(array $records): void { - + foreach ($records as $record) { - $ninja_invoice_data = $this->transformer->qbToNinja($record); + $ninja_invoice_data = $this->invoice_transformer->qbToNinja($record); $payment_ids = $ninja_invoice_data['payment_ids'] ?? []; @@ -49,7 +55,11 @@ class QbInvoice implements SyncInterface unset($ninja_invoice_data['payment_ids']); - if ($invoice = $this->findInvoice($ninja_invoice_data)) { + if ($invoice = $this->findInvoice($ninja_invoice_data['id'], $ninja_invoice_data['client_id'])) { + + if($invoice->id) + $this->processQbToNinjaInvoiceUpdate($ninja_invoice_data, $invoice); + $invoice->fill($ninja_invoice_data); $invoice->saveQuietly(); @@ -95,17 +105,38 @@ class QbInvoice implements SyncInterface } + private function processQbToNinjaInvoiceUpdate(array $ninja_invoice_data, Invoice $invoice): void + { + $current_ninja_invoice_balance = $invoice->balance; + $qb_invoice_balance = $ninja_invoice_data['balance']; - private function findInvoice(array $ninja_invoice_data): ?Invoice + if(floatval($current_ninja_invoice_balance) == floatval($qb_invoice_balance)) + { + nlog('Invoice balance is the same, skipping update of line items'); + unset($ninja_invoice_data['line_items']); + $invoice->fill($ninja_invoice_data); + $invoice->saveQuietly(); + } + else{ + nlog('Invoice balance is different, updating line items'); + $this->invoice_repository->save($ninja_invoice_data, $invoice); + } + } + + private function findInvoice(string $id, ?string $client_id = null): ?Invoice { $search = Invoice::query() ->withTrashed() ->where('company_id', $this->service->company->id) - ->where('sync->qb_id', $ninja_invoice_data['id']); + ->where('sync->qb_id', $id); - if($search->count() == 0) { + if($search->count() == 0 && $client_id) { $invoice = InvoiceFactory::create($this->service->company->id, $this->service->company->owner()->id); - $invoice->client_id = $ninja_invoice_data['client_id']; + $invoice->client_id = $client_id; + + $sync = new InvoiceSync(); + $sync->qb_id = $id; + $invoice->sync = $sync; return $invoice; } elseif($search->count() == 1) { @@ -116,4 +147,25 @@ class QbInvoice implements SyncInterface } + + public function sync(string $id, string $last_updated): void + { + + $qb_record = $this->find($id); + + if($this->service->updateGate('invoice') && $invoice = $this->findInvoice($id)) + { + + //logic here to determine if we should update the record + if(Carbon::parse($last_updated)->gt(Carbon::parse($invoice->updated_at))) + { + $ninja_invoice_data = $this->invoice_transformer->qbToNinja($qb_record); + $this->invoice_repository->save($ninja_invoice_data, $invoice); + + } + + // } + } + } + } diff --git a/app/Services/Quickbooks/QuickbooksService.php b/app/Services/Quickbooks/QuickbooksService.php index f4d067256255..9f3b2e1befc9 100644 --- a/app/Services/Quickbooks/QuickbooksService.php +++ b/app/Services/Quickbooks/QuickbooksService.php @@ -90,11 +90,14 @@ class QuickbooksService private function checkToken(): self { - - if($this->company->quickbooks->accessTokenKey > time()) + nlog($this->company->quickbooks->accessTokenExpiresAt); + nlog(time()); + + if($this->company->quickbooks->accessTokenExpiresAt > time()) return $this; if($this->company->quickbooks->accessTokenExpiresAt < time() && $this->try_refresh){ + nlog('Refreshing token'); $this->sdk()->refreshToken($this->company->quickbooks->refresh_token); $this->company = $this->company->fresh(); $this->try_refresh = false; @@ -145,7 +148,10 @@ class QuickbooksService */ public function updateGate(string $entity): bool { - return (bool) $this->service->settings->{$entity}->sync && $this->service->settings->{$entity}->update_record; + nlog($this->settings->{$entity}->sync); + nlog($this->settings->{$entity}->update_record); + + return $this->settings->{$entity}->sync && $this->settings->{$entity}->update_record; } /** diff --git a/app/Services/Quickbooks/Transformers/InvoiceTransformer.php b/app/Services/Quickbooks/Transformers/InvoiceTransformer.php index d2c85a31b005..53588385e6c8 100644 --- a/app/Services/Quickbooks/Transformers/InvoiceTransformer.php +++ b/app/Services/Quickbooks/Transformers/InvoiceTransformer.php @@ -88,18 +88,38 @@ class InvoiceTransformer extends BaseTransformer foreach($qb_items as $qb_item) { - $item = new InvoiceItem; - $item->product_key = data_get($qb_item, 'SalesItemLineDetail.ItemRef.name', ''); - $item->notes = data_get($qb_item,'Description', ''); - $item->quantity = data_get($qb_item,'SalesItemLineDetail.Qty', 0); - $item->cost = data_get($qb_item, 'SalesItemLineDetail.UnitPrice', 0); - $item->discount = data_get($item,'DiscountRate', data_get($qb_item,'DiscountAmount', 0)); - $item->is_amount_discount = data_get($qb_item,'DiscountAmount', 0) > 0 ? true : false; - $item->type_id = stripos(data_get($qb_item, 'ItemAccountRef.name') ?? '', 'Service') !== false ? '2' : '1'; - $item->tax_id = data_get($qb_item, 'TaxCodeRef.value', '') == 'NON' ? Product::PRODUCT_TYPE_EXEMPT : $item->type_id; - $item->tax_rate1 = data_get($qb_item, 'TxnTaxDetail.TaxLine.TaxLineDetail.TaxPercent', 0); - $item->tax_name1 = $item->tax_rate1 > 0 ? "Sales Tax" : ""; - $items[] = (object)$item; + + if(data_get($qb_item, 'DetailType.value') == 'SalesItemLineDetail') + { + $item = new InvoiceItem; + $item->product_key = data_get($qb_item, 'SalesItemLineDetail.ItemRef.name', ''); + $item->notes = data_get($qb_item,'Description', ''); + $item->quantity = data_get($qb_item,'SalesItemLineDetail.Qty', 0); + $item->cost = data_get($qb_item, 'SalesItemLineDetail.UnitPrice', 0); + $item->discount = data_get($item,'DiscountRate', data_get($qb_item,'DiscountAmount', 0)); + $item->is_amount_discount = data_get($qb_item,'DiscountAmount', 0) > 0 ? true : false; + $item->type_id = stripos(data_get($qb_item, 'ItemAccountRef.name') ?? '', 'Service') !== false ? '2' : '1'; + $item->tax_id = data_get($qb_item, 'TaxCodeRef.value', '') == 'NON' ? Product::PRODUCT_TYPE_EXEMPT : $item->type_id; + $item->tax_rate1 = data_get($qb_item, 'TxnTaxDetail.TaxLine.TaxLineDetail.TaxPercent', 0); + $item->tax_name1 = $item->tax_rate1 > 0 ? "Sales Tax" : ""; + $items[] = (object)$item; + } + + if(data_get($qb_item, 'DetailType.value') == 'DiscountLineDetail') + { + + $item = new InvoiceItem(); + $item->product_key = ctrans('texts.discount'); + $item->notes = ctrans('texts.discount'); + $item->quantity = 1; + $item->cost = data_get($qb_item, 'Amount', 0) * -1; + $item->discount = 0; + $item->is_amount_discount = true; + $item->type_id = '1'; + $item->tax_id = Product::PRODUCT_TYPE_PHYSICAL; + $items[] = (object)$item; + + } } nlog($items);