diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index 56a388dfea6d..3b9a2d54fe6c 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -86,8 +86,8 @@ class Rule extends BaseRule implements RuleInterface if($this->client->is_tax_exempt) return $this->taxExempt(); - $this->tax_name1 = $this->vat_rate; - $this->tax_rate1 = "MwSt."; + $this->tax_rate1 = $this->vat_rate; + $this->tax_name1 = "MwSt."; return $this; @@ -186,13 +186,13 @@ class Rule extends BaseRule implements RuleInterface { $this->vat_rate = 0; $this->reduced_vat_rate = 0; - nlog("euro zone and tax exempt"); + // nlog("euro zone and tax exempt"); } elseif(!in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && ($this->foreign_consumer_tax_exempt || $this->foreign_business_tax_exempt)) //foreign + tax exempt { $this->vat_rate = 0; $this->reduced_vat_rate = 0; - nlog("foreign and tax exempt"); + // nlog("foreign and tax exempt"); } elseif(in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && !$this->client->has_valid_vat_number) //eu country / no valid vat { @@ -200,18 +200,18 @@ class Rule extends BaseRule implements RuleInterface { $this->vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->vat_rate; $this->reduced_vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_vat_rate; - nlog("eu zone with sales above threshold"); + // nlog("eu zone with sales above threshold"); } else { $this->vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->vat_rate; $this->reduced_vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_vat_rate; - nlog("same eu country with"); + // nlog("same eu country with"); } } else { - nlog("default tax"); $this->vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->vat_rate; $this->reduced_vat_rate = $this->client->company->tax_data->regions->EU->subregions->{$this->client->company->country()->iso_3166_2}->reduced_vat_rate; + // nlog("default tax"); } return $this; diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index adc8d562039e..732f343ee8b9 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -141,13 +141,16 @@ class InvoiceItemSum //should we be filtering by client country here? do we need to reflect at the company <=> client level? if (in_array($this->client->country->iso_3166_2, $this->tax_jurisdictions)) { //only calculate for supported tax jurisdictions - $class = "App\DataMapper\Tax\\".$this->client->country->iso_3166_2."\\Rule"; + $class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule"; $tax_data = new Response($this->invoice->tax_data); $this->rule = new $class(); - $this->rule->setTaxData($tax_data); - $this->rule->setClient($this->client); + $this->rule + ->setTaxData($tax_data) + ->setClient($this->client) + ->init(); + $this->calc_tax = true; return $this; @@ -196,7 +199,7 @@ class InvoiceItemSum */ private function calcTaxesAutomatically(): self { - if ($this->invoice->company->tax_all_products || $this->item->tax_id != '') { + if ($this->invoice->company->tax_all_products || ( property_exists($this->item, 'tax_id') && $this->item->tax_id != '')) { $this->rule->tax(); } else { $this->rule->init()->taxByType($this->item->tax_id); @@ -250,7 +253,7 @@ class InvoiceItemSum $this->setTotalTaxes($this->formatValue($item_tax, $this->currency->precision)); $this->item->gross_line_total = $this->getLineTotal() + $item_tax; - + $this->item->tax_amount = $item_tax; return $this; diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index 6f8676cd14f7..f4504c4b35e4 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -14,10 +14,12 @@ namespace Tests\Unit\Tax; use Tests\TestCase; use App\Models\Client; use App\Models\Company; +use App\Models\Invoice; use Tests\MockAccountData; use App\DataMapper\Tax\DE\Rule; use App\DataMapper\Tax\TaxModel; use App\DataMapper\CompanySettings; +use App\DataMapper\Tax\ZipTax\Response; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -42,6 +44,202 @@ class EuTaxTest extends TestCase $this->makeTestData(); } + + public function testInvoiceTaxCalcDetoBeNoVat() + { + $settings = CompanySettings::defaults(); + $settings->country_id = '276'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_region = 'DE'; + $tax_data->seller_subregion = 'DE'; + $tax_data->regions->EU->has_sales_above_threshold = true; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'tax_all_products' => true, + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 56, + 'shipping_country_id' => 56, + 'has_valid_vat_number' => false, + ]); + + $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_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response([]), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(21, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(121, $invoice->amount); + } + + public function testInvoiceTaxCalcDetoBe() + { + $settings = CompanySettings::defaults(); + $settings->country_id = '276'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_region = 'DE'; + $tax_data->seller_subregion = 'DE'; + $tax_data->regions->EU->has_sales_above_threshold = true; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'tax_all_products' => true, + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 56, + 'shipping_country_id' => 56, + 'has_valid_vat_number' => 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_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response([]), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(0, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(100, $invoice->amount); + } + + + public function testInvoiceTaxCalcDetoDe() + { + $settings = CompanySettings::defaults(); + $settings->country_id = '276'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_region = 'DE'; + $tax_data->seller_subregion = 'DE'; + $tax_data->regions->EU->has_sales_above_threshold = true; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + 'calculate_taxes' => true, + 'tax_all_products' => true, + ]); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 276, + 'shipping_country_id' => 276, + 'has_valid_vat_number' => 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_rate1' => 0, + 'tax_rate2' => 0, + 'tax_rate3' => 0, + 'tax_name1' => '', + 'tax_name2' => '', + 'tax_name3' => '', + 'tax_data' => new Response([]), + ]); + + $invoice = $invoice->calc()->getInvoice(); + + $this->assertEquals(19, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(119, $invoice->amount); + + } + + public function testCorrectRuleInit() { @@ -296,7 +494,7 @@ class EuTaxTest extends TestCase } - public function testTaxExemption1() + public function testTaxExemptionDeSellerBeBuyer() { $settings = CompanySettings::defaults(); $settings->country_id = '276'; // germany @@ -337,7 +535,7 @@ class EuTaxTest extends TestCase } - public function testTaxExemption2() + public function testTaxExemptionDeSellerDeBuyer() { $settings = CompanySettings::defaults(); $settings->country_id = '276'; // germany