diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index a184f2ac211b..136fbf0bfcea 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -147,8 +147,6 @@ class BaseRule implements RuleInterface $this->configTaxData(); - nlog($this->invoice->tax_data); - $this->tax_data = new Response($this->invoice->tax_data); return $this; diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index 9538a4f3085e..4a4527057b9f 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -129,6 +129,7 @@ class Rule extends BaseRule implements RuleInterface */ public function taxShipping($item): self { + if($this->tax_data?->txbFreight == 'Y') { $this->default($item); } @@ -144,6 +145,8 @@ class Rule extends BaseRule implements RuleInterface */ public function taxPhysical($item): self { + nlog("tax physical"); + nlog($item); $this->default($item); return $this; diff --git a/app/Utils/Traits/CleanLineItems.php b/app/Utils/Traits/CleanLineItems.php index 921f66c32e1b..19938c0dce0b 100644 --- a/app/Utils/Traits/CleanLineItems.php +++ b/app/Utils/Traits/CleanLineItems.php @@ -66,7 +66,12 @@ trait CleanLineItems $item['tax_id'] = '1'; } elseif(array_key_exists('tax_id', $item) && $item['tax_id'] == '') { - $item['tax_id'] = '1'; + + if($item['type_id'] == '2') + $item['tax_id'] = '2'; + else + $item['tax_id'] = '1'; + } } diff --git a/tests/Unit/Tax/UsTaxTest.php b/tests/Unit/Tax/UsTaxTest.php index b1af855f06d3..15a0c10f1d5a 100644 --- a/tests/Unit/Tax/UsTaxTest.php +++ b/tests/Unit/Tax/UsTaxTest.php @@ -149,6 +149,521 @@ class UsTaxTest extends TestCase return $invoice; } + public function testTaxAuNoExemption() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = false; + $tax_data->regions->AU->has_sales_above_threshold = true; + $tax_data->regions->AU->tax_all_subregions = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 36, + 'postal_code' => '30002', + 'shipping_country_id' => 36, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => false, + 'state' => 'NSW' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_PHYSICAL, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(110, $invoice->amount); + + } + + public function testTaxAuClientExemption() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = false; + $tax_data->regions->AU->has_sales_above_threshold = true; + $tax_data->regions->AU->tax_all_subregions = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 36, + 'postal_code' => '30002', + 'shipping_country_id' => 36, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => true, + 'state' => 'NSW' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_PHYSICAL, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $invoice->amount); + + } + + public function testTaxAuProductExemption() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = false; + $tax_data->regions->AU->has_sales_above_threshold = true; + $tax_data->regions->AU->tax_all_subregions = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 36, + 'postal_code' => '30002', + 'shipping_country_id' => 36, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => false, + 'state' => 'NSW' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_EXEMPT, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $invoice->amount); + + } + + public function testTaxAuProductOverride() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = false; + $tax_data->regions->AU->has_sales_above_threshold = true; + $tax_data->regions->AU->tax_all_subregions = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 36, + 'postal_code' => '30002', + 'shipping_country_id' => 36, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => false, + 'state' => 'NSW' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => 'OVERRIDE', + 'tax_rate1' => 20, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_OVERRIDE_TAX, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(120, $invoice->amount); + + } + + public function testInterstateFreightNoTaxWithProductTax() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 840, + 'postal_code' => '30002', + 'shipping_country_id' => 840, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => false, + 'state' => 'GA' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_SHIPPING, + ], + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_PHYSICAL, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(208.75, $invoice->amount); + + } + + public function testInterstateFreightProductNoTax() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = false; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 840, + 'postal_code' => '30002', + 'shipping_country_id' => 840, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => false, + 'state' => 'GA' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_SHIPPING, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $invoice->amount); + + } + + + + public function testInterstateServiceProductNoTax() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = false; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 840, + 'postal_code' => '30002', + 'shipping_country_id' => 840, + 'shipping_postal_code' => '30002', + 'shipping_state' => '30002', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => false, + 'state' => 'GA' + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '2', + 'tax_id' => Product::PRODUCT_TYPE_SERVICE, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $invoice->amount); + + } + + public function testInterstateWithNoTax() { @@ -219,8 +734,6 @@ class UsTaxTest extends TestCase } - - public function testSameSubregionAndExemptProduct() { @@ -293,6 +806,78 @@ class UsTaxTest extends TestCase } + public function testSameSubregionAndExemptClient() + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all_subregions = true; + $tax_data->regions->EU->has_sales_above_threshold = true; + $tax_data->regions->EU->tax_all_subregions = true; + $tax_data->regions->EU->subregions->DE->tax_rate = 21; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'origin_tax_data' => new Response($this->mock_response), + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 840, + 'postal_code' => '90210', + 'shipping_country_id' => 840, + 'shipping_postal_code' => '90210', + 'has_valid_vat_number' => false, + 'is_tax_exempt' => true, + ]); + + $invoice = Invoice::factory()->create([ + 'company_id' => $company->id, + 'client_id' => $client->id, + 'status_id' => 1, + 'user_id' => $this->user->id, + 'uses_inclusive_taxes' => false, + 'discount' => 0, + 'line_items' => [ + [ + 'product_key' => 'Test', + 'notes' => 'Test', + 'cost' => 100, + 'quantity' => 1, + 'tax_name1' => '', + 'tax_rate1' => 0, + 'tax_name2' => '', + 'tax_rate2' => 0, + 'tax_name3' => '', + 'tax_rate3' => 0, + 'type_id' => '1', + 'tax_id' => Product::PRODUCT_TYPE_PHYSICAL, + ], + ], + 'tax_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(100, $invoice->amount); + + } + + public function testForeignTaxesEnabledWithExemptProduct() { $settings = CompanySettings::defaults();