mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
QB Sync
This commit is contained in:
parent
94fb81c57b
commit
f5226d342f
@ -34,6 +34,10 @@ class QuickbooksSync
|
|||||||
|
|
||||||
public QuickbooksSyncMap $expense;
|
public QuickbooksSyncMap $expense;
|
||||||
|
|
||||||
|
public string $default_income_account = '';
|
||||||
|
|
||||||
|
public string $default_expense_account = '';
|
||||||
|
|
||||||
public function __construct(array $attributes = [])
|
public function __construct(array $attributes = [])
|
||||||
{
|
{
|
||||||
$this->client = new QuickbooksSyncMap($attributes['client'] ?? []);
|
$this->client = new QuickbooksSyncMap($attributes['client'] ?? []);
|
||||||
@ -45,5 +49,7 @@ class QuickbooksSync
|
|||||||
$this->product = new QuickbooksSyncMap($attributes['product'] ?? []);
|
$this->product = new QuickbooksSyncMap($attributes['product'] ?? []);
|
||||||
$this->payment = new QuickbooksSyncMap($attributes['payment'] ?? []);
|
$this->payment = new QuickbooksSyncMap($attributes['payment'] ?? []);
|
||||||
$this->expense = new QuickbooksSyncMap($attributes['expense'] ?? []);
|
$this->expense = new QuickbooksSyncMap($attributes['expense'] ?? []);
|
||||||
|
$this->default_income_account = $attributes['default_income_account'] ?? '';
|
||||||
|
$this->default_expense_account = $attributes['default_expense_account'] ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -108,6 +108,9 @@ class QbInvoice implements SyncInterface
|
|||||||
|
|
||||||
$invoice = $this->findInvoice($id);
|
$invoice = $this->findInvoice($id);
|
||||||
|
|
||||||
|
nlog("Comparing QB last updated: " . $last_updated);
|
||||||
|
nlog("Comparing Ninja last updated: " . $invoice->updated_at);
|
||||||
|
|
||||||
if(data_get($qb_record, 'TxnStatus') === 'Voided')
|
if(data_get($qb_record, 'TxnStatus') === 'Voided')
|
||||||
{
|
{
|
||||||
$this->delete($id);
|
$this->delete($id);
|
||||||
@ -117,7 +120,7 @@ class QbInvoice implements SyncInterface
|
|||||||
if(!$invoice->id){
|
if(!$invoice->id){
|
||||||
$this->syncNinjaInvoice($qb_record);
|
$this->syncNinjaInvoice($qb_record);
|
||||||
}
|
}
|
||||||
elseif(Carbon::parse($last_updated)->gt(Carbon::parse($invoice->updated_at)))
|
elseif(Carbon::parse($last_updated)->gt(Carbon::parse($invoice->updated_at)) || $qb_record->SyncToken == '0')
|
||||||
{
|
{
|
||||||
$ninja_invoice_data = $this->invoice_transformer->qbToNinja($qb_record);
|
$ninja_invoice_data = $this->invoice_transformer->qbToNinja($qb_record);
|
||||||
nlog($ninja_invoice_data);
|
nlog($ninja_invoice_data);
|
||||||
|
@ -84,6 +84,43 @@ class QuickbooksService
|
|||||||
|
|
||||||
$this->settings = $this->company->quickbooks->settings;
|
$this->settings = $this->company->quickbooks->settings;
|
||||||
|
|
||||||
|
$this->checkDefaultAccounts();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkDefaultAccounts(): self
|
||||||
|
{
|
||||||
|
|
||||||
|
$accountQuery = "SELECT * FROM Account WHERE AccountType IN ('Income', 'Cost of Goods Sold')";
|
||||||
|
|
||||||
|
if(strlen($this->settings->default_income_account) == 0 || strlen($this->settings->default_expense_account) == 0){
|
||||||
|
|
||||||
|
nlog("Checking default accounts for company {$this->company->company_key}");
|
||||||
|
$accounts = $this->sdk->Query($accountQuery);
|
||||||
|
|
||||||
|
nlog($accounts);
|
||||||
|
|
||||||
|
$find_income_account = true;
|
||||||
|
$find_expense_account = true;
|
||||||
|
|
||||||
|
foreach ($accounts as $account) {
|
||||||
|
if ($account->AccountType->value == 'Income' && $find_income_account) {
|
||||||
|
$this->settings->default_income_account = $account->Id->value;
|
||||||
|
$find_income_account = false;
|
||||||
|
} elseif ($account->AccountType->value == 'Cost of Goods Sold' && $find_expense_account) {
|
||||||
|
$this->settings->default_expense_account = $account->Id->value;
|
||||||
|
$find_expense_account = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nlog($this->settings);
|
||||||
|
|
||||||
|
$this->company->quickbooks->settings = $this->settings;
|
||||||
|
$this->company->save();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,12 +11,16 @@ use App\Models\Invoice;
|
|||||||
use App\Models\Product;
|
use App\Models\Product;
|
||||||
use Tests\MockAccountData;
|
use Tests\MockAccountData;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
|
use App\Models\ClientContact;
|
||||||
use App\DataMapper\ClientSync;
|
use App\DataMapper\ClientSync;
|
||||||
use App\DataMapper\InvoiceItem;
|
use App\DataMapper\InvoiceItem;
|
||||||
|
use App\DataMapper\InvoiceSync;
|
||||||
|
use App\DataMapper\ProductSync;
|
||||||
use App\Utils\Traits\MakesHash;
|
use App\Utils\Traits\MakesHash;
|
||||||
use App\Import\Providers\Quickbooks;
|
use App\Import\Providers\Quickbooks;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
|
use QuickBooksOnline\API\Facades\Item;
|
||||||
use App\Import\Transformer\BaseTransformer;
|
use App\Import\Transformer\BaseTransformer;
|
||||||
use App\Services\Quickbooks\QuickbooksService;
|
use App\Services\Quickbooks\QuickbooksService;
|
||||||
use Illuminate\Routing\Middleware\ThrottleRequests;
|
use Illuminate\Routing\Middleware\ThrottleRequests;
|
||||||
@ -31,6 +35,7 @@ class QuickbooksTest extends TestCase
|
|||||||
protected $quickbooks;
|
protected $quickbooks;
|
||||||
protected $data;
|
protected $data;
|
||||||
protected QuickbooksService $qb;
|
protected QuickbooksService $qb;
|
||||||
|
protected $faker;
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
{
|
{
|
||||||
@ -44,44 +49,140 @@ class QuickbooksTest extends TestCase
|
|||||||
$this->markTestSkipped('No need to run this test on Travis');
|
$this->markTestSkipped('No need to run this test on Travis');
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->makeTestData();
|
$this->faker = \Faker\Factory::create();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createQbProduct()
|
||||||
|
{
|
||||||
|
$service_product = Product::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->company->owner()->id,
|
||||||
|
'notes' => $this->faker->sentence(),
|
||||||
|
'product_key' => $this->faker->word(63),
|
||||||
|
'tax_id' => 2,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$non_inventory_product = Product::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->company->owner()->id,
|
||||||
|
'notes' => $this->faker->sentence(),
|
||||||
|
'product_key' => $this->faker->word(63),
|
||||||
|
'tax_id' => 1,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$qb_product = Item::create([
|
||||||
|
"Name" => $non_inventory_product->product_key,
|
||||||
|
"Description" => $non_inventory_product->notes,
|
||||||
|
"Active" => true,
|
||||||
|
"FullyQualifiedName" => $non_inventory_product->product_key,
|
||||||
|
"Taxable" => true,
|
||||||
|
"UnitPrice" => $non_inventory_product->price,
|
||||||
|
"Type" => "NonInventory",
|
||||||
|
"IncomeAccountRef" => [
|
||||||
|
"value" => $this->qb->settings->default_income_account, // Replace with your actual income account ID
|
||||||
|
],
|
||||||
|
// "AssetAccountRef" => [
|
||||||
|
// "value" => "81", // Replace with your actual asset account ID
|
||||||
|
// "name" => "Inventory Asset"
|
||||||
|
// ],
|
||||||
|
// "InvStartDate" => date('Y-m-d'),
|
||||||
|
// "QtyOnHand" => 100,
|
||||||
|
"TrackQtyOnHand" => false,
|
||||||
|
// "ExpenseAccountRef" => [
|
||||||
|
// "value" => "80", // Replace with your actual COGS account ID
|
||||||
|
// "name" => "Cost of Goods Sold"
|
||||||
|
// ]
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$qb_product = $this->qb->sdk->Add($qb_product);
|
||||||
|
|
||||||
|
$sync = new ProductSync();
|
||||||
|
$sync->qb_id = data_get($qb_product, 'Id.value');
|
||||||
|
$non_inventory_product->sync = $sync;
|
||||||
|
$non_inventory_product->save();
|
||||||
|
|
||||||
|
$qb_service = Item::create([
|
||||||
|
"Name" => $service_product->product_key,
|
||||||
|
"Description" => $service_product->notes,
|
||||||
|
"Active" => true,
|
||||||
|
"FullyQualifiedName" => $service_product->product_key,
|
||||||
|
"Taxable" => true,
|
||||||
|
"UnitPrice" => $service_product->price,
|
||||||
|
"Type" => "Service",
|
||||||
|
"IncomeAccountRef" => [
|
||||||
|
"value" => $this->qb->settings->default_income_account, // Replace with your actual income account ID
|
||||||
|
],
|
||||||
|
"TrackQtyOnHand" => false,
|
||||||
|
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
$qb_service = $this->qb->sdk->Add($qb_service);
|
||||||
|
|
||||||
|
$sync = new ProductSync();
|
||||||
|
$sync->qb_id = data_get($qb_service, 'Id.value');
|
||||||
|
$service_product->sync = $sync;
|
||||||
|
$service_product->save();
|
||||||
|
|
||||||
|
return [$non_inventory_product, $service_product];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createQbCustomer()
|
public function createQbCustomer()
|
||||||
{
|
{
|
||||||
|
$client = Client::factory()->create([
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'user_id' => $this->company->owner()->id,
|
||||||
|
'address1' => $this->faker->address(),
|
||||||
|
'city' => $this->faker->city(),
|
||||||
|
'state' => $this->faker->state(),
|
||||||
|
'postal_code' => $this->faker->postcode(),
|
||||||
|
'country_id' => 840,
|
||||||
|
'shipping_country_id' => 840,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$contact = ClientContact::factory()->create([
|
||||||
|
'user_id' => $this->company->owner()->id,
|
||||||
|
'client_id' => $client->id,
|
||||||
|
'company_id' => $this->company->id,
|
||||||
|
'is_primary' => 1,
|
||||||
|
'send_email' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
$customerData = [
|
$customerData = [
|
||||||
"DisplayName" => $this->client->present()->name(), // Required and must be unique
|
"DisplayName" => $client->present()->name(), // Required and must be unique
|
||||||
"PrimaryEmailAddr" => [
|
"PrimaryEmailAddr" => [
|
||||||
"Address" => $this->client->present()->email(),
|
"Address" => $client->present()->email(),
|
||||||
],
|
],
|
||||||
"PrimaryPhone" => [
|
"PrimaryPhone" => [
|
||||||
"FreeFormNumber" => $this->client->present()->phone()
|
"FreeFormNumber" => $client->present()->phone()
|
||||||
],
|
],
|
||||||
"CompanyName" => $this->client->present()->name(),
|
"CompanyName" => $client->present()->name(),
|
||||||
"BillAddr" => [
|
"BillAddr" => [
|
||||||
"Line1" => $this->client->address1 ?? '',
|
"Line1" => $client->address1 ?? '',
|
||||||
"City" => $this->client->city ?? '',
|
"City" => $client->city ?? '',
|
||||||
"CountrySubDivisionCode" => $this->client->state ?? '',
|
"CountrySubDivisionCode" => $client->state ?? '',
|
||||||
"PostalCode" => $this->client->postal_code ?? '',
|
"PostalCode" => $client->postal_code ?? '',
|
||||||
"Country" => $this->client->country->iso_3166_3
|
"Country" => $client->country->iso_3166_3
|
||||||
],
|
],
|
||||||
"ShipAddr" => [
|
"ShipAddr" => [
|
||||||
"Line1" => $this->client->shipping_address1 ?? '',
|
"Line1" => $client->shipping_address1 ?? '',
|
||||||
"City" => $this->client->shipping_city ?? '',
|
"City" => $client->shipping_city ?? '',
|
||||||
"CountrySubDivisionCode" => $this->client->shipping_state ?? '',
|
"CountrySubDivisionCode" => $client->shipping_state ?? '',
|
||||||
"PostalCode" => $this->client->shipping_postal_code ?? '',
|
"PostalCode" => $client->shipping_postal_code ?? '',
|
||||||
"Country" => $this->client->shipping_country->iso_3166_3
|
"Country" => $client->shipping_country->iso_3166_3
|
||||||
],
|
],
|
||||||
"GivenName" => $this->client->present()->first_name(),
|
"GivenName" => $client->present()->first_name(),
|
||||||
"FamilyName" => $this->client->present()->last_name(),
|
"FamilyName" => $client->present()->last_name(),
|
||||||
"PrintOnCheckName" => $this->client->present()->primary_contact_name(),
|
"PrintOnCheckName" => $client->present()->primary_contact_name(),
|
||||||
"Notes" => $this->client->public_notes,
|
"Notes" => $client->public_notes,
|
||||||
// "TaxIdentifier" => $this->client->vat_number ?? '', // Federal Employer Identification Number (EIN)
|
// "TaxIdentifier" => $client->vat_number ?? '', // Federal Employer Identification Number (EIN)
|
||||||
"BusinessNumber" => $this->client->id_number ?? '',
|
"BusinessNumber" => $client->id_number ?? '',
|
||||||
"Active" => $this->client->deleted_at ? false : true,
|
"Active" => $client->deleted_at ? false : true,
|
||||||
"V4IDPseudonym" => $this->client->client_hash,
|
"V4IDPseudonym" => $client->client_hash,
|
||||||
"WebAddr" => $this->client->website ?? '',
|
"WebAddr" => $client->website ?? '',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
@ -93,10 +194,10 @@ class QuickbooksTest extends TestCase
|
|||||||
$sync = new ClientSync();
|
$sync = new ClientSync();
|
||||||
$sync->qb_id = data_get($resultingCustomerObj, 'Id.value');
|
$sync->qb_id = data_get($resultingCustomerObj, 'Id.value');
|
||||||
|
|
||||||
$this->client->sync = $sync;
|
$client->sync = $sync;
|
||||||
$this->client->save();
|
$client->save();
|
||||||
|
|
||||||
return [$resultingCustomerObj, $this->client->id];
|
return [$resultingCustomerObj, $client->id];
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,29 +246,32 @@ class QuickbooksTest extends TestCase
|
|||||||
$customer = $customer_payload[0];
|
$customer = $customer_payload[0];
|
||||||
$client_id = $customer_payload[1];
|
$client_id = $customer_payload[1];
|
||||||
|
|
||||||
|
$products = $this->createQbProduct();
|
||||||
|
|
||||||
|
$non_inventory_product = $products[0];
|
||||||
|
$service_product = $products[1];
|
||||||
|
|
||||||
|
|
||||||
nlog(data_get($customer, 'Id.value'));
|
nlog(data_get($customer, 'Id.value'));
|
||||||
|
|
||||||
$client = Client::find($client_id);
|
$client = Client::find($client_id);
|
||||||
|
|
||||||
nlog($client);
|
|
||||||
|
|
||||||
$item_product = new InvoiceItem();
|
$item_product = new InvoiceItem();
|
||||||
$item_product->product_key = $this->faker->word(); // Changed from randomWord() to word()
|
$item_product->product_key = $non_inventory_product->product_key; // Changed from randomWord() to word()
|
||||||
$item_product->notes = $this->faker->sentence();
|
$item_product->notes = $non_inventory_product->notes;
|
||||||
$item_product->quantity = 1;
|
$item_product->quantity = 1;
|
||||||
$item_product->cost = 10;
|
$item_product->cost = $non_inventory_product->price;
|
||||||
$item_product->line_total = 10;
|
$item_product->line_total = $non_inventory_product->price;
|
||||||
$item_product->type_id = '1';
|
$item_product->type_id = '1';
|
||||||
|
|
||||||
$item_service = new InvoiceItem();
|
$item_service = new InvoiceItem();
|
||||||
$item_service->product_key = $this->faker->word(); // Changed from randomWord() to word()
|
$item_service->product_key = $service_product->product_key; // Changed from randomWord() to word()
|
||||||
$item_service->notes = $this->faker->sentence();
|
$item_service->notes = $service_product->notes;
|
||||||
$item_service->quantity = 1;
|
$item_service->quantity = 1;
|
||||||
$item_service->cost = 20;
|
$item_service->cost = $service_product->price;
|
||||||
$item_service->line_total = 20;
|
$item_service->line_total = $service_product->price;
|
||||||
$item_service->type_id = '2';
|
$item_service->type_id = '2';
|
||||||
|
|
||||||
|
|
||||||
$i = Invoice::factory()->create([
|
$i = Invoice::factory()->create([
|
||||||
'company_id' => $this->company->id,
|
'company_id' => $this->company->id,
|
||||||
'user_id' => $client->user_id,
|
'user_id' => $client->user_id,
|
||||||
@ -186,37 +290,44 @@ class QuickbooksTest extends TestCase
|
|||||||
'is_amount_discount' => 0,
|
'is_amount_discount' => 0,
|
||||||
'discount' => 0,
|
'discount' => 0,
|
||||||
'line_items' => [$item_product, $item_service],
|
'line_items' => [$item_product, $item_service],
|
||||||
|
'number' => null,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$i->calc()->getInvoice()->service()->applyNumber()->markSent()->createInvitations()->save();
|
$i->calc()->getInvoice()->service()->applyNumber()->markSent()->createInvitations()->save();
|
||||||
|
|
||||||
$this->assertEquals(2, $i->status_id);
|
$this->assertEquals(2, $i->status_id);
|
||||||
$this->assertEquals(30, $i->amount);
|
// $this->assertEquals(30, $i->amount);
|
||||||
$this->assertEquals(0, $i->paid_to_date);
|
$this->assertEquals(0, $i->paid_to_date);
|
||||||
$this->assertEquals(0, $i->is_amount_discount);
|
$this->assertEquals(0, $i->is_amount_discount);
|
||||||
$this->assertEquals(0, $i->discount);
|
$this->assertEquals(0, $i->discount);
|
||||||
$this->assertEquals(30, $i->balance);
|
// $this->assertEquals(30, $i->balance);
|
||||||
|
|
||||||
$line_items = [];
|
$line_items = [];
|
||||||
$line_num = 1;
|
$line_num = 1;
|
||||||
|
|
||||||
foreach($i->line_items as $line_item)
|
foreach($i->line_items as $line_item)
|
||||||
{
|
{
|
||||||
|
$product = Product::where('company_id', $this->company->id)
|
||||||
|
->where('product_key', $line_item->product_key)
|
||||||
|
->first();
|
||||||
|
|
||||||
|
$this->assertNotNull($product);
|
||||||
|
|
||||||
$line_items[] = [
|
$line_items[] = [
|
||||||
'LineNum' => $line_num, // Add the line number
|
'LineNum' => $line_num, // Add the line number
|
||||||
'DetailType' => 'SalesItemLineDetail',
|
'DetailType' => 'SalesItemLineDetail',
|
||||||
'SalesItemLineDetail' => [
|
'SalesItemLineDetail' => [
|
||||||
'ItemRef' => [
|
'ItemRef' => [
|
||||||
'value' => $line_item->product_key,
|
'value' => $product->sync->qb_id,
|
||||||
],
|
],
|
||||||
'Qty' => $line_item->quantity,
|
'Qty' => $line_item->quantity,
|
||||||
'UnitPrice' => $line_item->cost,
|
'UnitPrice' => $line_item->cost,
|
||||||
|
'TaxCodeRef' => [
|
||||||
|
'value' => (in_array($product->tax_id, [5, 8])) ? 'NON' : 'TAX',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
'Description' => $line_item->notes,
|
'Description' => $line_item->notes,
|
||||||
// 'Type' => $this->getQuickBooksItemType($line_item),
|
|
||||||
// 'PurchaseCost' => $line_item->product_cost,
|
|
||||||
// 'Taxable' => $line_item->isTaxable(),
|
|
||||||
'Amount' => $line_item->line_total,
|
'Amount' => $line_item->line_total,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -230,28 +341,46 @@ class QuickbooksTest extends TestCase
|
|||||||
"value" => data_get($customer, 'Id.value')
|
"value" => data_get($customer, 'Id.value')
|
||||||
],
|
],
|
||||||
"BillEmail" => [
|
"BillEmail" => [
|
||||||
"Address" => $c->present()->email()
|
"Address" => $client->present()->email()
|
||||||
],
|
],
|
||||||
"TxnDate" => $i->date,
|
"TxnDate" => $i->date,
|
||||||
"DueDate" => $i->due_date,
|
"DueDate" => $i->due_date,
|
||||||
"TotalAmt" => $i->amount,
|
"TotalAmt" => $i->amount,
|
||||||
"DocNumber" => $i->number,
|
"DocNumber" => $i->number,
|
||||||
|
"ApplyTaxAfterDiscount" => false,
|
||||||
|
"GlobalTaxCalculation" => "TaxExcluded", // This tells QuickBooks to calculate taxes
|
||||||
|
"TxnTaxDetail" => [
|
||||||
|
"UseAutomatedSalesTax" => true,
|
||||||
|
// "TxnTaxCodeRef" => [
|
||||||
|
// "value" => "TAX" // Use the appropriate tax code for your QuickBooks account
|
||||||
|
// "DefaultTaxRateRef" => [
|
||||||
|
// ]
|
||||||
|
]
|
||||||
// "Note" => $this->invoice->public_notes,
|
// "Note" => $this->invoice->public_notes,
|
||||||
];
|
];
|
||||||
|
|
||||||
$invoice = \QuickBooksOnline\API\Facades\Invoice::create($invoiceData);
|
nlog($invoiceData);
|
||||||
|
|
||||||
|
$invoice = \QuickBooksOnline\API\Facades\Invoice::create($invoiceData);
|
||||||
|
|
||||||
|
|
||||||
nlog($invoice);
|
nlog($invoice);
|
||||||
|
|
||||||
return $this->qb->sdk->Add($invoice);
|
$qb_invoice = $this->qb->sdk->Add($invoice);
|
||||||
|
|
||||||
|
$sync = new InvoiceSync();
|
||||||
|
$sync->qb_id = data_get($qb_invoice, 'Id.value');
|
||||||
|
$i->sync = $sync;
|
||||||
|
$i->save();
|
||||||
|
|
||||||
|
return $qb_invoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function getQuickBooksItemType($line_item): string
|
private function getQuickBooksItemType($line_item): string
|
||||||
{
|
{
|
||||||
$typeMap = [
|
$typeMap = [
|
||||||
'1' => 'Inventory', // product
|
'1' => 'NonInventory', // product
|
||||||
'2' => 'Service', // service
|
'2' => 'Service', // service
|
||||||
'3' => 'NonInventory', // unpaid gateway fee
|
'3' => 'NonInventory', // unpaid gateway fee
|
||||||
'4' => 'NonInventory', // paid gateway fee
|
'4' => 'NonInventory', // paid gateway fee
|
||||||
@ -265,15 +394,15 @@ class QuickbooksTest extends TestCase
|
|||||||
protected function tearDown(): void
|
protected function tearDown(): void
|
||||||
{
|
{
|
||||||
|
|
||||||
$this->company->clients()->forceDelete();
|
// $this->company->clients()->forceDelete();
|
||||||
$this->company->products()->forceDelete();
|
// $this->company->products()->forceDelete();
|
||||||
$this->company->projects()->forceDelete();
|
// $this->company->projects()->forceDelete();
|
||||||
$this->company->tasks()->forceDelete();
|
// $this->company->tasks()->forceDelete();
|
||||||
$this->company->vendors()->forceDelete();
|
// $this->company->vendors()->forceDelete();
|
||||||
$this->company->expenses()->forceDelete();
|
// $this->company->expenses()->forceDelete();
|
||||||
$this->company->purchase_orders()->forceDelete();
|
// $this->company->purchase_orders()->forceDelete();
|
||||||
$this->company->bank_transaction_rules()->forceDelete();
|
// $this->company->bank_transaction_rules()->forceDelete();
|
||||||
$this->company->bank_transactions()->forceDelete();
|
// $this->company->bank_transactions()->forceDelete();
|
||||||
|
|
||||||
parent::tearDown();
|
parent::tearDown();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user