mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-08 14:04:40 -04:00
Invoice Sync
This commit is contained in:
parent
6f3e56a2a2
commit
8fadba5545
@ -53,10 +53,10 @@ class QuickbooksImport implements ShouldQueue
|
|||||||
'product' => 'Item',
|
'product' => 'Item',
|
||||||
'client' => 'Customer',
|
'client' => 'Customer',
|
||||||
'invoice' => 'Invoice',
|
'invoice' => 'Invoice',
|
||||||
|
'sales' => 'SalesReceipt',
|
||||||
// 'quote' => 'Estimate',
|
// 'quote' => 'Estimate',
|
||||||
// 'purchase_order' => 'PurchaseOrder',
|
// 'purchase_order' => 'PurchaseOrder',
|
||||||
// 'payment' => 'Payment',
|
// 'payment' => 'Payment',
|
||||||
'sales' => 'SalesReceipt',
|
|
||||||
// 'vendor' => 'Vendor',
|
// 'vendor' => 'Vendor',
|
||||||
// 'expense' => 'Purchase',
|
// 'expense' => 'Purchase',
|
||||||
];
|
];
|
||||||
|
@ -11,33 +11,39 @@
|
|||||||
|
|
||||||
namespace App\Services\Quickbooks\Models;
|
namespace App\Services\Quickbooks\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
use App\Models\Invoice;
|
use App\Models\Invoice;
|
||||||
|
use App\DataMapper\InvoiceSync;
|
||||||
use App\Factory\InvoiceFactory;
|
use App\Factory\InvoiceFactory;
|
||||||
use App\Interfaces\SyncInterface;
|
use App\Interfaces\SyncInterface;
|
||||||
|
use App\Repositories\InvoiceRepository;
|
||||||
use App\Services\Quickbooks\QuickbooksService;
|
use App\Services\Quickbooks\QuickbooksService;
|
||||||
use App\Services\Quickbooks\Transformers\InvoiceTransformer;
|
use App\Services\Quickbooks\Transformers\InvoiceTransformer;
|
||||||
use App\Services\Quickbooks\Transformers\PaymentTransformer;
|
use App\Services\Quickbooks\Transformers\PaymentTransformer;
|
||||||
|
|
||||||
class QbInvoice implements SyncInterface
|
class QbInvoice implements SyncInterface
|
||||||
{
|
{
|
||||||
protected InvoiceTransformer $transformer;
|
protected InvoiceTransformer $invoice_transformer;
|
||||||
|
|
||||||
|
protected InvoiceRepository $invoice_repository;
|
||||||
|
|
||||||
public function __construct(public QuickbooksService $service)
|
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);
|
return $this->service->sdk->FindById('Invoice', $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function syncToNinja(array $records): void
|
public function syncToNinja(array $records): void
|
||||||
{
|
{
|
||||||
|
|
||||||
foreach ($records as $record) {
|
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'] ?? [];
|
$payment_ids = $ninja_invoice_data['payment_ids'] ?? [];
|
||||||
|
|
||||||
@ -49,7 +55,11 @@ class QbInvoice implements SyncInterface
|
|||||||
|
|
||||||
unset($ninja_invoice_data['payment_ids']);
|
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->fill($ninja_invoice_data);
|
||||||
$invoice->saveQuietly();
|
$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()
|
$search = Invoice::query()
|
||||||
->withTrashed()
|
->withTrashed()
|
||||||
->where('company_id', $this->service->company->id)
|
->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 = 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;
|
return $invoice;
|
||||||
} elseif($search->count() == 1) {
|
} 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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -90,11 +90,14 @@ class QuickbooksService
|
|||||||
|
|
||||||
private function checkToken(): self
|
private function checkToken(): self
|
||||||
{
|
{
|
||||||
|
nlog($this->company->quickbooks->accessTokenExpiresAt);
|
||||||
if($this->company->quickbooks->accessTokenKey > time())
|
nlog(time());
|
||||||
|
|
||||||
|
if($this->company->quickbooks->accessTokenExpiresAt > time())
|
||||||
return $this;
|
return $this;
|
||||||
|
|
||||||
if($this->company->quickbooks->accessTokenExpiresAt < time() && $this->try_refresh){
|
if($this->company->quickbooks->accessTokenExpiresAt < time() && $this->try_refresh){
|
||||||
|
nlog('Refreshing token');
|
||||||
$this->sdk()->refreshToken($this->company->quickbooks->refresh_token);
|
$this->sdk()->refreshToken($this->company->quickbooks->refresh_token);
|
||||||
$this->company = $this->company->fresh();
|
$this->company = $this->company->fresh();
|
||||||
$this->try_refresh = false;
|
$this->try_refresh = false;
|
||||||
@ -145,7 +148,10 @@ class QuickbooksService
|
|||||||
*/
|
*/
|
||||||
public function updateGate(string $entity): bool
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -88,18 +88,38 @@ class InvoiceTransformer extends BaseTransformer
|
|||||||
|
|
||||||
foreach($qb_items as $qb_item)
|
foreach($qb_items as $qb_item)
|
||||||
{
|
{
|
||||||
$item = new InvoiceItem;
|
|
||||||
$item->product_key = data_get($qb_item, 'SalesItemLineDetail.ItemRef.name', '');
|
if(data_get($qb_item, 'DetailType.value') == 'SalesItemLineDetail')
|
||||||
$item->notes = data_get($qb_item,'Description', '');
|
{
|
||||||
$item->quantity = data_get($qb_item,'SalesItemLineDetail.Qty', 0);
|
$item = new InvoiceItem;
|
||||||
$item->cost = data_get($qb_item, 'SalesItemLineDetail.UnitPrice', 0);
|
$item->product_key = data_get($qb_item, 'SalesItemLineDetail.ItemRef.name', '');
|
||||||
$item->discount = data_get($item,'DiscountRate', data_get($qb_item,'DiscountAmount', 0));
|
$item->notes = data_get($qb_item,'Description', '');
|
||||||
$item->is_amount_discount = data_get($qb_item,'DiscountAmount', 0) > 0 ? true : false;
|
$item->quantity = data_get($qb_item,'SalesItemLineDetail.Qty', 0);
|
||||||
$item->type_id = stripos(data_get($qb_item, 'ItemAccountRef.name') ?? '', 'Service') !== false ? '2' : '1';
|
$item->cost = data_get($qb_item, 'SalesItemLineDetail.UnitPrice', 0);
|
||||||
$item->tax_id = data_get($qb_item, 'TaxCodeRef.value', '') == 'NON' ? Product::PRODUCT_TYPE_EXEMPT : $item->type_id;
|
$item->discount = data_get($item,'DiscountRate', data_get($qb_item,'DiscountAmount', 0));
|
||||||
$item->tax_rate1 = data_get($qb_item, 'TxnTaxDetail.TaxLine.TaxLineDetail.TaxPercent', 0);
|
$item->is_amount_discount = data_get($qb_item,'DiscountAmount', 0) > 0 ? true : false;
|
||||||
$item->tax_name1 = $item->tax_rate1 > 0 ? "Sales Tax" : "";
|
$item->type_id = stripos(data_get($qb_item, 'ItemAccountRef.name') ?? '', 'Service') !== false ? '2' : '1';
|
||||||
$items[] = (object)$item;
|
$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);
|
nlog($items);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user