From 66772e38975449ae691a9d7f5b427cd5ca40870e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Mar 2023 07:40:44 +1100 Subject: [PATCH 01/38] calculate taxes --- app/Helpers/Invoice/InvoiceItemSum.php | 32 ++++++++++++++++++++++++-- tests/Unit/Tax/SumTaxTest.php | 5 ++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 464679e2f5e2..0f61cc97231e 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -11,9 +11,10 @@ namespace App\Helpers\Invoice; -use App\DataMapper\BaseSettings; -use App\DataMapper\InvoiceItem; +use App\Models\Client; use App\Models\Invoice; +use App\DataMapper\InvoiceItem; +use App\DataMapper\BaseSettings; use App\Utils\Traits\NumberFormatter; class InvoiceItemSum @@ -48,6 +49,10 @@ class InvoiceItemSum private $tax_collection; + private ?Client $client; + + private bool $calc_tax = false; + public function __construct($invoice) { $this->tax_collection = collect([]); @@ -56,6 +61,8 @@ class InvoiceItemSum if ($this->invoice->client) { $this->currency = $this->invoice->client->currency(); + $this->client = $this->invoice->client; + $this->calc_tax = $this->shouldCalculateTax(); } else { $this->currency = $this->invoice->vendor->currency(); } @@ -89,6 +96,17 @@ class InvoiceItemSum return $this; } + private function shouldCalculateTax(): bool + { + if(!$this->invoice->company->calculate_taxes) + return false; + + if(in_array($this->client->country->iso_3166_2, ['US'])) + return true; + + return false; + } + private function push() { $this->sub_total += $this->getLineTotal(); @@ -121,6 +139,16 @@ class InvoiceItemSum return $this; } + + /** + * Attempts to calculate taxes based on the clients location + * + * @return self + */ + private function calcTaxesAutomatically() + { + + } private function calcTaxes() { diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index 73cfcc06ffa2..996ce63eeebd 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -79,6 +79,11 @@ class SumTaxTest extends TestCase // $this->makeTestData(); } + public function testTaxOnInvoice() + { + + } + public function testSumOfInvoice() { From b5a4fc9865a6df00a2eafe58c4d57dffcdf9f4c2 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Mar 2023 13:56:26 +1100 Subject: [PATCH 02/38] Working on taxes --- app/DataMapper/Tax/ZipTax/Response.php | 45 +++++++-- app/Helpers/Invoice/InvoiceItemSum.php | 1 + tests/Unit/Tax/SumTaxTest.php | 125 +++++++++++++------------ 3 files changed, 104 insertions(+), 67 deletions(-) diff --git a/app/DataMapper/Tax/ZipTax/Response.php b/app/DataMapper/Tax/ZipTax/Response.php index dfeea6e1f1cd..0b4b60724ef8 100644 --- a/app/DataMapper/Tax/ZipTax/Response.php +++ b/app/DataMapper/Tax/ZipTax/Response.php @@ -18,8 +18,7 @@ class Response public int $rCode = 100; /** - * ["results" => [ - * [ + * [ * "geoPostalCode" => "92582", * "geoCity" => "SAN JACINTO", * "geoCounty" => "RIVERSIDE", @@ -54,13 +53,45 @@ class Response * "district5SalesTax" => 0, * "district5UseTax" => 0, * "originDestination" => "D", - * ], - * ] + * * ]; - * - * @var mixed[] + * */ - public array $results = []; + + public string $geoPostalCode = "92582"; + public string $geoCity = "SAN JACINTO"; + public string $geoCounty = "RIVERSIDE"; + public string $geoState = "CA"; + public float $taxSales = 0.0875; + public float $taxUse = 0.0875; + public string $txbService = "N"; + public string $txbFreight = "N"; + public float $stateSalesTax = 0.06; + public float $stateUseTax = 0.06; + public float $citySalesTax = 0.01; + public float $cityUseTax = 0.01; + public string $cityTaxCode = "874"; + public float $countySalesTax = 0.0025; + public float $countyUseTax = 0.0025; + public string $countyTaxCode = ""; + public float $districtSalesTax = 0.015; + public float $districtUseTax = 0.015; + public string $district1Code = "26"; + public float $district1SalesTax = 0; + public float $district1UseTax = 0; + public string $district2Code = "26"; + public float $district2SalesTax = 0.005; + public float $district2UseTax = 0.005; + public string $district3Code = ""; + public float $district3SalesTax = 0; + public float $district3UseTax = 0; + public string $district4Code = "33"; + public float $district4SalesTax = 0.01; + public float $district4UseTax = 0.01; + public string $district5Code = ""; + public float $district5SalesTax = 0; + public float $district5UseTax = 0; + public string $originDestination = "D"; } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 0f61cc97231e..ed27bc4229e4 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -148,6 +148,7 @@ class InvoiceItemSum private function calcTaxesAutomatically() { + return $this; } private function calcTaxes() diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index 996ce63eeebd..c5fa17ef0f5a 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -12,6 +12,7 @@ namespace Tests\Unit\Tax; use Tests\TestCase; +use App\Models\Client; use Tests\MockAccountData; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -24,45 +25,41 @@ class SumTaxTest extends TestCase use MockAccountData; use DatabaseTransactions; - public array $response = - ["results" => [ - [ - "geoPostalCode" => "92582", - "geoCity" => "SAN JACINTO", - "geoCounty" => "RIVERSIDE", - "geoState" => "CA", - "taxSales" => 0.0875, - "taxUse" => 0.0875, // tax amount where destination does not charge sales tax, but origin does - "txbService" => "N", // whether services are taxed in this locale - "txbFreight" => "N", // whether freight is taxes in this locale - "stateSalesTax" => 0.06, - "stateUseTax" => 0.06, - "citySalesTax" => 0.01, - "cityUseTax" => 0.01, - "cityTaxCode" => "874", - "countySalesTax" => 0.0025, - "countyUseTax" => 0.0025, - "countyTaxCode" => "", - "districtSalesTax" => 0.015, - "districtUseTax" => 0.015, - "district1Code" => "26", - "district1SalesTax" => 0, - "district1UseTax" => 0, - "district2Code" => "26", - "district2SalesTax" => 0.005, - "district2UseTax" => 0.005, - "district3Code" => "", - "district3SalesTax" => 0, - "district3UseTax" => 0, - "district4Code" => "33", - "district4SalesTax" => 0.01, - "district4UseTax" => 0.01, - "district5Code" => "", - "district5SalesTax" => 0, - "district5UseTax" => 0, //district1-5 portion of the district tax - "originDestination" => "D", //location where this is taxed origin/destination/null - ], - ] + public array $response = [ + "geoPostalCode" => "92582", + "geoCity" => "SAN JACINTO", + "geoCounty" => "RIVERSIDE", + "geoState" => "CA", + "taxSales" => 0.0875, + "taxUse" => 0.0875, // tax amount where destination does not charge sales tax, but origin does + "txbService" => "N", // whether services are taxed in this locale + "txbFreight" => "N", // whether freight is taxes in this locale + "stateSalesTax" => 0.06, + "stateUseTax" => 0.06, + "citySalesTax" => 0.01, + "cityUseTax" => 0.01, + "cityTaxCode" => "874", + "countySalesTax" => 0.0025, + "countyUseTax" => 0.0025, + "countyTaxCode" => "", + "districtSalesTax" => 0.015, + "districtUseTax" => 0.015, + "district1Code" => "26", + "district1SalesTax" => 0, + "district1UseTax" => 0, + "district2Code" => "26", + "district2SalesTax" => 0.005, + "district2UseTax" => 0.005, + "district3Code" => "", + "district3SalesTax" => 0, + "district3UseTax" => 0, + "district4Code" => "33", + "district4SalesTax" => 0.01, + "district4UseTax" => 0.01, + "district5Code" => "", + "district5SalesTax" => 0, + "district5UseTax" => 0, //district1-5 portion of the district tax + "originDestination" => "D", //location where this is taxed origin/destination/null ]; @@ -76,42 +73,50 @@ class SumTaxTest extends TestCase $this->withoutExceptionHandling(); - // $this->makeTestData(); + $this->makeTestData(); } public function testTaxOnInvoice() { - + $c = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + ]); + + $c->tax_data = $this->response; + $c->save(); + + $this->assertEquals("92582", $c->tax_data->geoPostalCode); } public function testSumOfInvoice() { - $this->assertEquals("CA", $this->response['results'][0]['geoState']); + $this->assertEquals("CA", $this->response['geoState']); } public function testSumOfTaxes() { $sum = - $this->response['results'][0]['stateSalesTax'] + - // $this->response['results'][0]['stateUseTax'] + - $this->response['results'][0]['citySalesTax'] + - // $this->response['results'][0]['cityUseTax'] + - $this->response['results'][0]['countySalesTax'] + - // $this->response['results'][0]['countyUseTax'] + - $this->response['results'][0]['districtSalesTax']; - // // $this->response['results'][0]['districtUseTax'] + - // $this->response['results'][0]['district1SalesTax'] + - // // $this->response['results'][0]['district1UseTax'] + - // $this->response['results'][0]['district2SalesTax'] + - // // $this->response['results'][0]['district2UseTax'] + - // $this->response['results'][0]['district3SalesTax'] + - // // $this->response['results'][0]['district3UseTax'] + - // $this->response['results'][0]['district4SalesTax'] + - // // $this->response['results'][0]['district4UseTax'] + - // $this->response['results'][0]['district5SalesTax']; - // $this->response['results'][0]['district5UseTax']; + $this->response['stateSalesTax'] + + // $this->response['stateUseTax'] + + $this->response['citySalesTax'] + + // $this->response['cityUseTax'] + + $this->response['countySalesTax'] + + // $this->response['countyUseTax'] + + $this->response['districtSalesTax']; + // // $this->response['districtUseTax'] + + // $this->response['district1SalesTax'] + + // // $this->response['district1UseTax'] + + // $this->response['district2SalesTax'] + + // // $this->response['district2UseTax'] + + // $this->response['district3SalesTax'] + + // // $this->response['district3UseTax'] + + // $this->response['district4SalesTax'] + + // // $this->response['district4UseTax'] + + // $this->response['district5SalesTax']; + // $this->response['district5UseTax']; $this->assertEquals(0.0875, $sum); } From bba25f103312f0b9ae73ffed73cbf415ffadc322 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Mar 2023 13:57:36 +1100 Subject: [PATCH 03/38] Taxes - sum --- tests/Unit/Tax/SumTaxTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index c5fa17ef0f5a..bb46cfec669e 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -87,6 +87,7 @@ class SumTaxTest extends TestCase $c->save(); $this->assertEquals("92582", $c->tax_data->geoPostalCode); + $this->assertEquals(0.0875, $c->tax_data->taxSales); } public function testSumOfInvoice() From 49b31826392e4dffbbd01a050fd68778aa56c308 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Mar 2023 16:43:09 +1100 Subject: [PATCH 04/38] Tests for taxes --- app/DataMapper/Tax/ZipTax/Response.php | 59 +++++++++++++---------- tests/Unit/Tax/SumTaxTest.php | 67 ++++++++++++++++++++++---- 2 files changed, 91 insertions(+), 35 deletions(-) diff --git a/app/DataMapper/Tax/ZipTax/Response.php b/app/DataMapper/Tax/ZipTax/Response.php index 0b4b60724ef8..e32d2586e16d 100644 --- a/app/DataMapper/Tax/ZipTax/Response.php +++ b/app/DataMapper/Tax/ZipTax/Response.php @@ -58,40 +58,49 @@ class Response * */ - public string $geoPostalCode = "92582"; - public string $geoCity = "SAN JACINTO"; - public string $geoCounty = "RIVERSIDE"; - public string $geoState = "CA"; - public float $taxSales = 0.0875; - public float $taxUse = 0.0875; - public string $txbService = "N"; - public string $txbFreight = "N"; - public float $stateSalesTax = 0.06; - public float $stateUseTax = 0.06; - public float $citySalesTax = 0.01; - public float $cityUseTax = 0.01; - public string $cityTaxCode = "874"; - public float $countySalesTax = 0.0025; - public float $countyUseTax = 0.0025; + public string $geoPostalCode = ""; + public string $geoCity = ""; + public string $geoCounty = ""; + public string $geoState = ""; + public float $taxSales = 0; + public float $taxUse = 0; + public string $txbService = ""; + public string $txbFreight = ""; + public float $stateSalesTax = 0; + public float $stateUseTax = 0; + public float $citySalesTax = 0; + public float $cityUseTax = 0; + public string $cityTaxCode = ""; + public float $countySalesTax = 0; + public float $countyUseTax = 0; public string $countyTaxCode = ""; - public float $districtSalesTax = 0.015; - public float $districtUseTax = 0.015; - public string $district1Code = "26"; + public float $districtSalesTax = 0; + public float $districtUseTax = 0; + public string $district1Code = ""; public float $district1SalesTax = 0; public float $district1UseTax = 0; - public string $district2Code = "26"; - public float $district2SalesTax = 0.005; - public float $district2UseTax = 0.005; + public string $district2Code = ""; + public float $district2SalesTax = 0; + public float $district2UseTax = 0; public string $district3Code = ""; public float $district3SalesTax = 0; public float $district3UseTax = 0; - public string $district4Code = "33"; - public float $district4SalesTax = 0.01; - public float $district4UseTax = 0.01; + public string $district4Code = ""; + public float $district4SalesTax = 0; + public float $district4UseTax = 0; public string $district5Code = ""; public float $district5SalesTax = 0; public float $district5UseTax = 0; - public string $originDestination = "D"; + public string $originDestination = ""; + + public function __construct(?array $data) + { + + foreach($data as $key => $value){ + $this->{$key} = $value; + } + + } } diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index bb46cfec669e..61c2df20c039 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -13,7 +13,12 @@ namespace Tests\Unit\Tax; use Tests\TestCase; use App\Models\Client; +use App\Models\Invoice; use Tests\MockAccountData; +use App\DataMapper\Tax\ClientTaxData; +use App\DataMapper\Tax\CompanyTaxData; +use App\DataMapper\Tax\InvoiceTaxData; +use App\DataMapper\Tax\ZipTax\Response; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -25,7 +30,9 @@ class SumTaxTest extends TestCase use MockAccountData; use DatabaseTransactions; - public array $response = [ + public Response $response; + + public array $resp = [ "geoPostalCode" => "92582", "geoCity" => "SAN JACINTO", "geoCounty" => "RIVERSIDE", @@ -74,39 +81,79 @@ class SumTaxTest extends TestCase $this->withoutExceptionHandling(); $this->makeTestData(); + + $this->response = new Response($this->resp); + } - public function testTaxOnInvoice() + public function testTaxOnCompany() + { + + $tax_class = new CompanyTaxData($this->response); + + $this->company->tax_data = $tax_class; + $this->company->save(); + + $this->assertEquals("92582", $this->company->tax_data->origin->geoPostalCode); + $this->assertEquals(0.0875, $this->company->tax_data->origin->taxSales); + + } + + public function testTaxOnClient() { $c = Client::factory()->create([ 'user_id' => $this->user->id, 'company_id' => $this->company->id, ]); + + $tax_class = new ClientTaxData($this->response, $this->response); - $c->tax_data = $this->response; + $c->tax_data = $tax_class; $c->save(); - $this->assertEquals("92582", $c->tax_data->geoPostalCode); - $this->assertEquals(0.0875, $c->tax_data->taxSales); + $this->assertEquals("92582", $c->tax_data->origin->geoPostalCode); + $this->assertEquals(0.0875, $c->tax_data->origin->taxSales); + + } + + public function testTaxOnInvoice() + { + + $i = Invoice::factory()->create([ + 'company_id' => $this->company->id, + 'client_id' => $this->client->id, + 'user_id' => $this->user->id, + ]); + + $tax_class = new InvoiceTaxData($this->response); + + $i->tax_data = $tax_class; + $i->save(); + + + $this->assertEquals("92582", $i->tax_data->origin->geoPostalCode); + $this->assertEquals(0.0875, $i->tax_data->origin->taxSales); + + } public function testSumOfInvoice() { - $this->assertEquals("CA", $this->response['geoState']); + $this->assertEquals("CA", $this->response->geoState); } public function testSumOfTaxes() { $sum = - $this->response['stateSalesTax'] + + $this->response->stateSalesTax + // $this->response['stateUseTax'] + - $this->response['citySalesTax'] + + $this->response->citySalesTax + // $this->response['cityUseTax'] + - $this->response['countySalesTax'] + + $this->response->countySalesTax + // $this->response['countyUseTax'] + - $this->response['districtSalesTax']; + $this->response->districtSalesTax; // // $this->response['districtUseTax'] + // $this->response['district1SalesTax'] + // // $this->response['district1UseTax'] + From aa51299a10d804d058046d2786c10b1d901493be Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Mar 2023 18:02:34 +1100 Subject: [PATCH 05/38] Working on auto tax calculations --- app/DataMapper/InvoiceItem.php | 4 + app/DataMapper/Tax/RuleInterface.php | 14 ++++ app/DataMapper/Tax/ZipTax/Response.php | 4 +- app/DataMapper/Tax/us/Rule.php | 78 ++++++++++++++++++- app/Helpers/Invoice/InvoiceItemSum.php | 38 ++++++++- app/Models/Account.php | 4 + app/Models/BankAccount.php | 1 + app/Models/BankIntegration.php | 1 + app/Models/Client.php | 19 +++++ app/Models/ClientContact.php | 5 ++ app/Models/Company.php | 44 +++++++++++ app/Models/CompanyGateway.php | 1 + app/Models/CompanyUser.php | 3 + app/Models/Credit.php | 7 ++ app/Models/Expense.php | 1 + app/Models/GatewayType.php | 1 + app/Models/GroupSetting.php | 2 + app/Models/Invoice.php | 9 +++ app/Models/Payment.php | 5 ++ app/Models/Product.php | 3 +- app/Models/Project.php | 2 + app/Models/Proposal.php | 1 + app/Models/PurchaseOrder.php | 6 ++ app/Models/Quote.php | 4 + app/Models/RecurringExpense.php | 1 + app/Models/RecurringInvoice.php | 5 ++ app/Models/RecurringQuote.php | 5 ++ app/Models/Task.php | 1 + app/Models/User.php | 7 ++ app/Models/Vendor.php | 4 + app/Models/VendorContact.php | 2 + ...054758_add_client_is_exempt_from_taxes.php | 30 +++++++ tests/Unit/Tax/SumTaxTest.php | 20 ++--- 33 files changed, 310 insertions(+), 22 deletions(-) create mode 100644 database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php diff --git a/app/DataMapper/InvoiceItem.php b/app/DataMapper/InvoiceItem.php index 228d2d0d5577..49571d668fdf 100644 --- a/app/DataMapper/InvoiceItem.php +++ b/app/DataMapper/InvoiceItem.php @@ -59,7 +59,11 @@ class InvoiceItem public $type_id = '1'; //1 = product, 2 = service, 3 unpaid gateway fee, 4 paid gateway fee, 5 late fee, 6 expense + public $tax_id = ''; + + public static $casts = [ + 'tax_id' => 'string', 'type_id' => 'string', 'quantity' => 'float', 'cost' => 'float', diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index ec88cd72c67d..6d322aea206e 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -14,4 +14,18 @@ namespace App\DataMapper\Tax; interface RuleInterface { public function run(); + + public function taxByType(?int $type); + + public function taxExempt(); + + public function taxDigital(); + + public function taxService(); + + public function taxShipping(); + + public function taxPhysical(); + + public function default(); } \ No newline at end of file diff --git a/app/DataMapper/Tax/ZipTax/Response.php b/app/DataMapper/Tax/ZipTax/Response.php index e32d2586e16d..457ea18d4966 100644 --- a/app/DataMapper/Tax/ZipTax/Response.php +++ b/app/DataMapper/Tax/ZipTax/Response.php @@ -64,8 +64,8 @@ class Response public string $geoState = ""; public float $taxSales = 0; public float $taxUse = 0; - public string $txbService = ""; - public string $txbFreight = ""; + public string $txbService = ""; // N = No, Y = Yes + public string $txbFreight = ""; // N = No, Y = Yes public float $stateSalesTax = 0; public float $stateUseTax = 0; public float $citySalesTax = 0; diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index c5211dcba0fe..d6738bb8a927 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -11,7 +11,10 @@ namespace App\DataMapper\Tax\us; -class Rule +use App\Models\Product; +use App\DataMapper\Tax\RuleInterface; + +class Rule implements RuleInterface { public float $al_sales_tax_rate = 4; // Alabama @@ -67,4 +70,77 @@ class Rule public float $dc_sales_tax_rate = 6; // District of Columbia public float $pr_sales_tax_rate = 11.5; // Puerto Rico + public string $tax_name1 = ''; + public float $tax_rate1 = 0; + + public string $tax_name2 = ''; + public float $tax_rate2 = 0; + + public string $tax_name3 = ''; + public float $tax_rate3 = 0; + + public function __construct(public RuleInterface $tax_data) + { + $this->tax_data = $tax_data; + } + + public function run() + { + $this->tax_name1 = $this->tax_data->taxSales * 100; + $this->tax_rate1 = "{$this->tax_data->geoState} Sales Tax"; + + } + + public function taxByType(?int $product_tax_type) + { + if(!$product_tax_type) + return; + + match($product_tax_type){ + Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), + Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), + Product::PRODUCT_TYPE_SERVICE => $this->taxService(), + Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), + Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), + default => $this->default(), + }; + + return $this; + } + + public function taxExempt() + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + } + + public function taxDigital() + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + } + + public function taxService() + { + if($this->tax_data->txbService == 'Y') + $this->run(); + } + + public function taxShipping() + { + if($this->tax_data->txbFreight == 'N') + $this->run(); + } + + public function taxPhysical() + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + } + + public function default() + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + } } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index ed27bc4229e4..1863edfed5f9 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -15,6 +15,7 @@ use App\Models\Client; use App\Models\Invoice; use App\DataMapper\InvoiceItem; use App\DataMapper\BaseSettings; +use App\DataMapper\Tax\RuleInterface; use App\Utils\Traits\NumberFormatter; class InvoiceItemSum @@ -53,6 +54,8 @@ class InvoiceItemSum private bool $calc_tax = false; + private RuleInterface $rule; + public function __construct($invoice) { $this->tax_collection = collect([]); @@ -98,11 +101,18 @@ class InvoiceItemSum private function shouldCalculateTax(): bool { - if(!$this->invoice->company->calculate_taxes) + if(!$this->invoice->company->calculate_taxes || $this->client->is_tax_exempt) return false; - if(in_array($this->client->country->iso_3166_2, ['US'])) + if(in_array($this->client->country->iso_3166_2, ['US'])){ //only calculate for USA + + $class = "App\DataMapper\Tax\\".strtolower($this->client->country->iso_3166_2)."\\Rule"; + + $this->rule = new $class($this->invoice->tax_data); + return true; + + } return false; } @@ -145,14 +155,34 @@ class InvoiceItemSum * * @return self */ - private function calcTaxesAutomatically() + private function calcTaxesAutomatically(): self { - + if($this->invoice->company->tax_all_products || $this->item->tax_id != ''){ + $this->rule->run(); + + } + else { + + $this->rule->taxByType($this->item->tax_id); + } + + $this->item->tax_name1 = $this->rule->tax_name1; + $this->item->tax_rate1 = $this->rule->tax_rate1; + + $this->item->tax_name2 = $this->rule->tax_name2; + $this->item->tax_rate2 = $this->rule->tax_rate2; + + $this->item->tax_name3 = $this->rule->tax_name3; + $this->item->tax_rate3 = $this->rule->tax_rate3; + return $this; } private function calcTaxes() { + if($this->calc_tax) + $this->calcTaxesAutomatically(); + $item_tax = 0; $amount = $this->item->line_total - ($this->item->line_total * ($this->invoice->discount / 100)); diff --git a/app/Models/Account.php b/app/Models/Account.php index 5514b7143d1a..b213e0b3b094 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -142,6 +142,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $companies * @property-read \Illuminate\Database\Eloquent\Collection $company_users * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class Account extends BaseModel diff --git a/app/Models/BankAccount.php b/app/Models/BankAccount.php index bf5743792368..aa285cc7c0b5 100644 --- a/app/Models/BankAccount.php +++ b/app/Models/BankAccount.php @@ -35,6 +35,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts + * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @mixin \Eloquent */ class BankAccount extends BaseModel diff --git a/app/Models/BankIntegration.php b/app/Models/BankIntegration.php index 2c66f8c5ad43..abcdd5e87bb4 100644 --- a/app/Models/BankIntegration.php +++ b/app/Models/BankIntegration.php @@ -78,6 +78,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions + * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @mixin \Eloquent */ class BankIntegration extends BaseModel diff --git a/app/Models/Client.php b/app/Models/Client.php index 1a0ce79f91b2..4c29cb728a74 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -221,6 +221,25 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices * @property-read \Illuminate\Database\Eloquent\Collection $system_logs * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property int $is_tax_exempt + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @method static \Illuminate\Database\Eloquent\Builder|Client whereIsTaxExempt($value) * @mixin \Eloquent */ class Client extends BaseModel implements HasLocalePreference diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 57a61715d5b3..72d71c689a60 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -138,6 +138,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations * @mixin \Eloquent */ class ClientContact extends Authenticatable implements HasLocalePreference diff --git a/app/Models/Company.php b/app/Models/Company.php index 983f6c8ae724..ea08cc9a63f8 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -420,6 +420,50 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read \Illuminate\Database\Eloquent\Collection $vendors * @property-read \Illuminate\Database\Eloquent\Collection $webhooks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_documents + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transaction_rules + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transactions + * @property-read \Illuminate\Database\Eloquent\Collection $client_contacts + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $company_gateways + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $designs + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expense_categories + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $group_settings + * @property-read \Illuminate\Database\Eloquent\Collection $groups + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $products + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_orders + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $system_log_relation + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $task_schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $task_statuses + * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $tax_rates + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $tokens_hashed + * @property-read \Illuminate\Database\Eloquent\Collection $user_designs + * @property-read \Illuminate\Database\Eloquent\Collection $user_payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $vendors + * @property-read \Illuminate\Database\Eloquent\Collection $webhooks * @mixin \Eloquent */ class Company extends BaseModel diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 4e7ebaf7c2c2..af59605cdb88 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -95,6 +95,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @mixin \Eloquent */ class CompanyGateway extends BaseModel diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index 17825b9ddc41..d53859d776a7 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -76,6 +76,9 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $token * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $token + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class CompanyUser extends Pivot diff --git a/app/Models/Credit.php b/app/Models/Credit.php index d649e894f582..c5872adc5a48 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -206,6 +206,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class Credit extends BaseModel diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 5bb420eca56a..a1d64852d2c4 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -133,6 +133,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Expense extends BaseModel diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 5b8b42aa0fd1..5363a62c0929 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -31,6 +31,7 @@ namespace App\Models; * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods + * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel diff --git a/app/Models/GroupSetting.php b/app/Models/GroupSetting.php index 0e065066a673..c71932c91a5f 100644 --- a/app/Models/GroupSetting.php +++ b/app/Models/GroupSetting.php @@ -60,6 +60,8 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $clients * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class GroupSetting extends StaticModel diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 311674d0cd3a..4125d794aece 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -232,6 +232,15 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Invoice extends BaseModel diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 15c738413b64..48f6a9abd0ba 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -147,6 +147,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $paymentables + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $paymentables * @mixin \Eloquent */ class Payment extends BaseModel diff --git a/app/Models/Product.php b/app/Models/Product.php index 44deea41a940..30623b9c706b 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -101,6 +101,7 @@ use League\CommonMark\CommonMarkConverter; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @method static \Illuminate\Database\Eloquent\Builder|Product whereTaxId($value) * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Product extends BaseModel @@ -113,7 +114,7 @@ class Product extends BaseModel public const PRODUCT_TYPE_PHYSICAL = 1; public const PRODUCT_TYPE_SERVICE = 2; public const PRODUCT_TYPE_DIGITAL = 3; - public const PRODUCT_TYPE_FREIGHT = 4; + public const PRODUCT_TYPE_SHIPPING = 4; public const PRODUCT_TAX_EXEMPT = 5; protected $fillable = [ diff --git a/app/Models/Project.php b/app/Models/Project.php index 4eb60947d7a8..5cd470447495 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -78,6 +78,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Project extends BaseModel diff --git a/app/Models/Proposal.php b/app/Models/Proposal.php index dd7133ee3004..8cc3cb023912 100644 --- a/app/Models/Proposal.php +++ b/app/Models/Proposal.php @@ -31,6 +31,7 @@ use App\Utils\Traits\MakesHash; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Proposal extends BaseModel diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index 1a82c57c6abe..a26d07be9fa9 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -198,6 +198,12 @@ use Illuminate\Support\Facades\Storage; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class PurchaseOrder extends BaseModel diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 71daf00ac449..96ae34f00539 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -193,6 +193,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @mixin \Eloquent */ class Quote extends BaseModel diff --git a/app/Models/RecurringExpense.php b/app/Models/RecurringExpense.php index d93e3f242a38..ec8bdf5d4e44 100644 --- a/app/Models/RecurringExpense.php +++ b/app/Models/RecurringExpense.php @@ -142,6 +142,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class RecurringExpense extends BaseModel diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 83eed4296d32..98741f9cb072 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -194,6 +194,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @mixin \Eloquent */ class RecurringInvoice extends BaseModel diff --git a/app/Models/RecurringQuote.php b/app/Models/RecurringQuote.php index 318bed3853b5..74d8f905d2cf 100644 --- a/app/Models/RecurringQuote.php +++ b/app/Models/RecurringQuote.php @@ -186,6 +186,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $quotes * @mixin \Eloquent */ class RecurringQuote extends BaseModel diff --git a/app/Models/Task.php b/app/Models/Task.php index 849d6cb9fa3f..30bbaa98e1ed 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -94,6 +94,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Task extends BaseModel diff --git a/app/Models/User.php b/app/Models/User.php index ae02be639d32..1ec59ba1325a 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -160,6 +160,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @mixin \Eloquent */ class User extends Authenticatable implements MustVerifyEmail diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 8d2df534ef38..4b5ddc137618 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -116,6 +116,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $contacts * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact * @mixin \Eloquent */ class Vendor extends BaseModel diff --git a/app/Models/VendorContact.php b/app/Models/VendorContact.php index c6514203e01f..4d3b0a83b126 100644 --- a/app/Models/VendorContact.php +++ b/app/Models/VendorContact.php @@ -116,6 +116,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @mixin \Eloquent */ class VendorContact extends Authenticatable implements HasLocalePreference diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php new file mode 100644 index 000000000000..44ba1fbc23a0 --- /dev/null +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -0,0 +1,30 @@ +boolean('is_tax_exempt')->default(false); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + + } +}; diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index 61c2df20c039..98dc46fc24ca 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -86,6 +86,12 @@ class SumTaxTest extends TestCase } + public function testCalcLogic() + { + $this->company->calculate_taxes = true; + $this->company->tax_all_products = true; + } + public function testTaxOnCompany() { @@ -148,23 +154,9 @@ class SumTaxTest extends TestCase { $sum = $this->response->stateSalesTax + - // $this->response['stateUseTax'] + $this->response->citySalesTax + - // $this->response['cityUseTax'] + $this->response->countySalesTax + - // $this->response['countyUseTax'] + $this->response->districtSalesTax; - // // $this->response['districtUseTax'] + - // $this->response['district1SalesTax'] + - // // $this->response['district1UseTax'] + - // $this->response['district2SalesTax'] + - // // $this->response['district2UseTax'] + - // $this->response['district3SalesTax'] + - // // $this->response['district3UseTax'] + - // $this->response['district4SalesTax'] + - // // $this->response['district4UseTax'] + - // $this->response['district5SalesTax']; - // $this->response['district5UseTax']; $this->assertEquals(0.0875, $sum); } From 680277f0629365f9d6a4334a42da7514a7ac9f2c Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 24 Mar 2023 18:58:59 +1100 Subject: [PATCH 06/38] Fixes for auto calculating taxes --- app/DataMapper/Tax/InvoiceTaxData.php | 3 + app/DataMapper/Tax/RuleInterface.php | 2 +- app/DataMapper/Tax/ZipTax/Response.php | 2 +- app/DataMapper/Tax/de/Rule.php | 87 +++++++++++++++++++++++++- app/DataMapper/Tax/us/Rule.php | 57 ++++++++++------- app/Helpers/Invoice/InvoiceItemSum.php | 51 ++++++++------- phpstan.neon | 3 +- tests/Unit/Tax/SumTaxTest.php | 84 ++++++++++++++++++++++++- 8 files changed, 240 insertions(+), 49 deletions(-) diff --git a/app/DataMapper/Tax/InvoiceTaxData.php b/app/DataMapper/Tax/InvoiceTaxData.php index f8e8667109a5..1c265f37fb06 100644 --- a/app/DataMapper/Tax/InvoiceTaxData.php +++ b/app/DataMapper/Tax/InvoiceTaxData.php @@ -24,5 +24,8 @@ class InvoiceTaxData public function __construct(public Response $origin) { + foreach($origin as $key => $value) { + $this->{$key} = $value; + } } } diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index 6d322aea206e..a9403451b539 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -13,7 +13,7 @@ namespace App\DataMapper\Tax; interface RuleInterface { - public function run(); + public function tax(); public function taxByType(?int $type); diff --git a/app/DataMapper/Tax/ZipTax/Response.php b/app/DataMapper/Tax/ZipTax/Response.php index 457ea18d4966..73b510b74954 100644 --- a/app/DataMapper/Tax/ZipTax/Response.php +++ b/app/DataMapper/Tax/ZipTax/Response.php @@ -93,7 +93,7 @@ class Response public float $district5UseTax = 0; public string $originDestination = ""; - public function __construct(?array $data) + public function __construct($data) { foreach($data as $key => $value){ diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index 4332abfa81c9..e379a4ce47b5 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -11,7 +11,9 @@ namespace App\DataMapper\Tax\de; +use App\Models\Product; use App\DataMapper\Tax\RuleInterface; +use App\DataMapper\Tax\ZipTax\Response; class Rule implements RuleInterface { @@ -89,8 +91,91 @@ class Rule implements RuleInterface public bool $foreign_consumer_tax_exempt = true; - public function run() + public string $tax_name1 = ''; + public float $tax_rate1 = 0; + + public string $tax_name2 = ''; + public float $tax_rate2 = 0; + + public string $tax_name3 = ''; + public float $tax_rate3 = 0; + + + public function __construct(public Response $tax_data) { + $this->tax_data = $tax_data; + } + + public function tax(): self + { + $this->tax_name1 = 21; + $this->tax_rate1 = "VAT"; + + return $this; + + } + + public function taxByType(?int $product_tax_type): self + { + if(!$product_tax_type) + return $this; + + match($product_tax_type){ + Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), + Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), + Product::PRODUCT_TYPE_SERVICE => $this->taxService(), + Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), + Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), + default => $this->default(), + }; + + return $this; + } + + public function taxExempt(): self + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + + return $this; + } + + public function taxDigital(): self + { + $this->tax(); + + return $this; + } + + public function taxService(): self + { + if($this->tax_data->txbService == 'Y') + $this->tax(); + + return $this; + } + + public function taxShipping(): self + { + if($this->tax_data->txbFreight == 'Y') + $this->tax(); + + return $this; + } + + public function taxPhysical(): self + { + $this->tax(); + + return $this; + } + + public function default(): self + { + + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + return $this; } } diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index d6738bb8a927..6259faeb43ec 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -13,6 +13,7 @@ namespace App\DataMapper\Tax\us; use App\Models\Product; use App\DataMapper\Tax\RuleInterface; +use App\DataMapper\Tax\ZipTax\Response; class Rule implements RuleInterface { @@ -79,22 +80,25 @@ class Rule implements RuleInterface public string $tax_name3 = ''; public float $tax_rate3 = 0; - public function __construct(public RuleInterface $tax_data) + public function __construct(public Response $tax_data) { $this->tax_data = $tax_data; + nlog($tax_data); } - public function run() + public function tax(): self { - $this->tax_name1 = $this->tax_data->taxSales * 100; - $this->tax_rate1 = "{$this->tax_data->geoState} Sales Tax"; + $this->tax_rate1 = $this->tax_data->taxSales * 100; + $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; + + return $this; } - public function taxByType(?int $product_tax_type) + public function taxByType(?int $product_tax_type): self { if(!$product_tax_type) - return; + return $this; match($product_tax_type){ Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), @@ -108,39 +112,50 @@ class Rule implements RuleInterface return $this; } - public function taxExempt() + public function taxExempt(): self { $this->tax_name1 = ''; $this->tax_rate1 = 0; + + return $this; } - public function taxDigital() + public function taxDigital(): self { - $this->tax_name1 = ''; - $this->tax_rate1 = 0; + $this->tax(); + + return $this; } - public function taxService() + public function taxService(): self { if($this->tax_data->txbService == 'Y') - $this->run(); + $this->tax(); + + return $this; } - public function taxShipping() + public function taxShipping(): self { - if($this->tax_data->txbFreight == 'N') - $this->run(); + if($this->tax_data->txbFreight == 'Y') + $this->tax(); + + return $this; } - public function taxPhysical() + public function taxPhysical(): self { + $this->tax(); + + return $this; + } + + public function default(): self + { + $this->tax_name1 = ''; $this->tax_rate1 = 0; - } - public function default() - { - $this->tax_name1 = ''; - $this->tax_rate1 = 0; + return $this; } } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 1863edfed5f9..e6d389a468e7 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -17,6 +17,7 @@ use App\DataMapper\InvoiceItem; use App\DataMapper\BaseSettings; use App\DataMapper\Tax\RuleInterface; use App\Utils\Traits\NumberFormatter; +use App\DataMapper\Tax\ZipTax\Response; class InvoiceItemSum { @@ -55,7 +56,7 @@ class InvoiceItemSum private bool $calc_tax = false; private RuleInterface $rule; - + public function __construct($invoice) { $this->tax_collection = collect([]); @@ -65,7 +66,7 @@ class InvoiceItemSum if ($this->invoice->client) { $this->currency = $this->invoice->client->currency(); $this->client = $this->invoice->client; - $this->calc_tax = $this->shouldCalculateTax(); + $this->shouldCalculateTax(); } else { $this->currency = $this->invoice->vendor->currency(); } @@ -99,22 +100,29 @@ class InvoiceItemSum return $this; } - private function shouldCalculateTax(): bool + private function shouldCalculateTax(): self { - if(!$this->invoice->company->calculate_taxes || $this->client->is_tax_exempt) - return false; + if (!$this->invoice->company->calculate_taxes || $this->client->is_tax_exempt) { + $this->calc_tax = false; + nlog("returning false"); + return $this; + } - if(in_array($this->client->country->iso_3166_2, ['US'])){ //only calculate for USA - + if (in_array($this->client->country->iso_3166_2, ['US'])) { //only calculate for USA $class = "App\DataMapper\Tax\\".strtolower($this->client->country->iso_3166_2)."\\Rule"; - $this->rule = new $class($this->invoice->tax_data); + $tax_data = new Response($this->invoice->tax_data); - return true; - - } + $this->rule = new $class($tax_data); - return false; + nlog("returning true"); + + $this->calc_tax = true; + + return $this; + } + + return $this; } private function push() @@ -149,20 +157,17 @@ class InvoiceItemSum return $this; } - + /** - * Attempts to calculate taxes based on the clients location + * Attempts to calculate taxes based on the clients location * * @return self */ - private function calcTaxesAutomatically(): self + private function calcTaxesAutomatically(): self { - if($this->invoice->company->tax_all_products || $this->item->tax_id != ''){ - $this->rule->run(); - - } - else { - + if ($this->invoice->company->tax_all_products || $this->item->tax_id != '') { + $this->rule->tax(); + } else { $this->rule->taxByType($this->item->tax_id); } @@ -180,8 +185,9 @@ class InvoiceItemSum private function calcTaxes() { - if($this->calc_tax) + if ($this->calc_tax) { $this->calcTaxesAutomatically(); + } $item_tax = 0; @@ -190,7 +196,6 @@ class InvoiceItemSum $item_tax += $item_tax_rate1_total; - // if($item_tax_rate1_total != 0) if (strlen($this->item->tax_name1) > 1) { $this->groupTax($this->item->tax_name1, $this->item->tax_rate1, $item_tax_rate1_total); } diff --git a/phpstan.neon b/phpstan.neon index 772e2bd19fb3..0324dc8d0903 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -2,4 +2,5 @@ parameters: level: 2 paths: - app -# - tests + universalObjectCratesClasses: + - App\DataMapper\Tax\RuleInterface \ No newline at end of file diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index 98dc46fc24ca..91e50be5aaa1 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -14,11 +14,14 @@ namespace Tests\Unit\Tax; use Tests\TestCase; use App\Models\Client; use App\Models\Invoice; +use App\Models\Product; use Tests\MockAccountData; +use App\DataMapper\InvoiceItem; use App\DataMapper\Tax\ClientTaxData; use App\DataMapper\Tax\CompanyTaxData; use App\DataMapper\Tax\InvoiceTaxData; use App\DataMapper\Tax\ZipTax\Response; +use App\Factory\InvoiceFactory; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -86,10 +89,89 @@ class SumTaxTest extends TestCase } - public function testCalcLogic() + + public function testCalcInvoiceNoTax() { + $this->company->calculate_taxes = false; + $this->company->tax_all_products = true; + $this->company->save(); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'country_id' => 840, + ]); + + $invoice = InvoiceFactory::create($this->company->id, $this->user->id); + $invoice->client_id = $client->id; + $invoice->uses_inclusive_taxes = false; + + $line_items = []; + + $invoice->tax_data = new InvoiceTaxData($this->response); + + $line_item = new InvoiceItem(); + $line_item->quantity = 1; + $line_item->cost = 10; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_item->tax_id = Product::PRODUCT_TYPE_PHYSICAL; + $line_items[] = $line_item; + + $invoice->line_items = $line_items; + $invoice->save(); + + $invoice = $invoice->calc()->getInvoice(); + + $line_items = $invoice->line_items; + + + $this->assertEquals(10, $invoice->amount); + $this->assertEquals("", $line_items[0]->tax_name1); + $this->assertEquals(0, $line_items[0]->tax_rate1); + } + + + public function testCalcInvoiceTax() + { + $this->company->calculate_taxes = true; $this->company->tax_all_products = true; + $this->company->save(); + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $this->company->id, + 'country_id' => 840, + ]); + + $invoice = InvoiceFactory::create($this->company->id, $this->user->id); + $invoice->client_id = $client->id; + $invoice->uses_inclusive_taxes = false; + + $line_items = []; + + $invoice->tax_data = new InvoiceTaxData($this->response); + + $line_item = new InvoiceItem; + $line_item->quantity = 1; + $line_item->cost = 10; + $line_item->product_key = 'Test'; + $line_item->notes = 'Test'; + $line_item->tax_id = Product::PRODUCT_TYPE_PHYSICAL; + $line_items[] = $line_item; + + $invoice->line_items = $line_items; + $invoice->save(); + + $invoice = $invoice->calc()->getInvoice(); + + $line_items = $invoice->line_items; + + + $this->assertEquals(10.88, $invoice->amount); + $this->assertEquals("CA Sales Tax", $line_items[0]->tax_name1); + $this->assertEquals(8.75, $line_items[0]->tax_rate1); } public function testTaxOnCompany() From 40f53e3dde0446469578793eff03250716eb7bff Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Mar 2023 09:26:27 +1100 Subject: [PATCH 07/38] Refactor for taxes --- app/DataMapper/Tax/ClientTaxData.php | 28 ------------------- app/DataMapper/Tax/CompanyTaxData.php | 28 ------------------- .../Tax/{InvoiceTaxData.php => TaxData.php} | 2 +- tests/Unit/Tax/SumTaxTest.php | 18 ++++++------ 4 files changed, 9 insertions(+), 67 deletions(-) delete mode 100644 app/DataMapper/Tax/ClientTaxData.php delete mode 100644 app/DataMapper/Tax/CompanyTaxData.php rename app/DataMapper/Tax/{InvoiceTaxData.php => TaxData.php} (96%) diff --git a/app/DataMapper/Tax/ClientTaxData.php b/app/DataMapper/Tax/ClientTaxData.php deleted file mode 100644 index 3f92e8866781..000000000000 --- a/app/DataMapper/Tax/ClientTaxData.php +++ /dev/null @@ -1,28 +0,0 @@ -company->calculate_taxes = false; @@ -108,7 +106,7 @@ class SumTaxTest extends TestCase $line_items = []; - $invoice->tax_data = new InvoiceTaxData($this->response); + $invoice->tax_data = new TaxData($this->response); $line_item = new InvoiceItem(); $line_item->quantity = 1; @@ -131,7 +129,7 @@ class SumTaxTest extends TestCase $this->assertEquals(0, $line_items[0]->tax_rate1); } - + /** Proves that we do calc taxes automatically */ public function testCalcInvoiceTax() { @@ -151,7 +149,7 @@ class SumTaxTest extends TestCase $line_items = []; - $invoice->tax_data = new InvoiceTaxData($this->response); + $invoice->tax_data = new TaxData($this->response); $line_item = new InvoiceItem; $line_item->quantity = 1; @@ -177,7 +175,7 @@ class SumTaxTest extends TestCase public function testTaxOnCompany() { - $tax_class = new CompanyTaxData($this->response); + $tax_class = new TaxData($this->response); $this->company->tax_data = $tax_class; $this->company->save(); @@ -194,7 +192,7 @@ class SumTaxTest extends TestCase 'company_id' => $this->company->id, ]); - $tax_class = new ClientTaxData($this->response, $this->response); + $tax_class = new TaxData($this->response, $this->response); $c->tax_data = $tax_class; $c->save(); @@ -213,7 +211,7 @@ class SumTaxTest extends TestCase 'user_id' => $this->user->id, ]); - $tax_class = new InvoiceTaxData($this->response); + $tax_class = new TaxData($this->response); $i->tax_data = $tax_class; $i->save(); From 3ed45ad27f29db3d1260d3957c9ab47b418559a8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 25 Mar 2023 11:02:43 +1100 Subject: [PATCH 08/38] Refactor for taxes --- app/DataMapper/Tax/us/Rule.php | 2 +- app/Helpers/Invoice/InvoiceItemSum.php | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index 6259faeb43ec..7fee2564dac3 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -153,7 +153,7 @@ class Rule implements RuleInterface public function default(): self { - $this->tax_name1 = ''; + $this->tax_name1 = 'Tax Exempt'; $this->tax_rate1 = 0; return $this; diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index e6d389a468e7..f093962f7f98 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -104,7 +104,6 @@ class InvoiceItemSum { if (!$this->invoice->company->calculate_taxes || $this->client->is_tax_exempt) { $this->calc_tax = false; - nlog("returning false"); return $this; } @@ -115,8 +114,6 @@ class InvoiceItemSum $this->rule = new $class($tax_data); - nlog("returning true"); - $this->calc_tax = true; return $this; From 046a72326e5acc8b74d35d9026d2357cea9de894 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 27 Mar 2023 07:14:10 +1100 Subject: [PATCH 09/38] Tax Providers --- app/DataMapper/Tax/de/Rule.php | 2 +- .../Tax/Providers/TaxProviderInterface.php | 20 ++++ app/Services/Tax/Providers/ZipTax.php | 13 ++- app/Services/Tax/TaxService.php | 93 +++++++++++++++++++ config/services.php | 6 ++ 5 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 app/Services/Tax/Providers/TaxProviderInterface.php diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index e379a4ce47b5..6097338b9d7e 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -108,7 +108,7 @@ class Rule implements RuleInterface public function tax(): self { - $this->tax_name1 = 21; + $this->tax_name1 = $this->vat_rate; $this->tax_rate1 = "VAT"; return $this; diff --git a/app/Services/Tax/Providers/TaxProviderInterface.php b/app/Services/Tax/Providers/TaxProviderInterface.php new file mode 100644 index 000000000000..bd2a60dbaba9 --- /dev/null +++ b/app/Services/Tax/Providers/TaxProviderInterface.php @@ -0,0 +1,20 @@ +api_key = $api_key; + + return $this; + } + /** * callApi * diff --git a/app/Services/Tax/TaxService.php b/app/Services/Tax/TaxService.php index 8d29eb299129..20ef2f042013 100644 --- a/app/Services/Tax/TaxService.php +++ b/app/Services/Tax/TaxService.php @@ -13,13 +13,106 @@ namespace App\Services\Tax; use App\Models\Client; use App\Models\Company; +use App\Services\Tax\Providers\ZipTax; class TaxService { + private string $provider = ZipTax::class; + + private mixed $api_credentials; public function __construct(protected Company $company, protected Client $client) { } + public function updateCompanyTaxData(): self + { + $this->configureProvider($this->provider); //hard coded for now to one provider, but we'll be able to swap these out later + + $company_details = [ + 'address1' => $this->company->settings->address1, + 'address2' => $this->company->settings->address2, + 'city' => $this->company->settings->city, + 'state' => $this->company->settings->state, + 'postal_code' => $this->company->settings->postal_code, + 'country_id' => $this->company->settings->country_id, + ]; + + $tax_provider = new $this->provider($company_details); + + $tax_provider->setApiCredentials($this->api_credentials); + + $tax_data = $tax_provider->run(); + + $this->company->tax_data = $tax_data; + + $this->company->save(); + + return $this; + + } + + public function updateClientTaxData(): self + { + $this->configureProvider($this->provider); //hard coded for now to one provider, but we'll be able to swap these out later + + $billing_details =[ + 'address1' => $this->client->address1, + 'address2' => $this->client->address2, + 'city' => $this->client->city, + 'state' => $this->client->state, + 'postal_code' => $this->client->postal_code, + 'country_id' => $this->client->country_id, + ]; + + $shipping_details =[ + 'address1' => $this->client->shipping_address1, + 'address2' => $this->client->shipping_address2, + 'city' => $this->client->shipping_city, + 'state' => $this->client->shipping_state, + 'postal_code' => $this->client->shipping_postal_code, + 'country_id' => $this->client->shipping_country_id, + ]; + + + $tax_provider = new $this->provider(); + + $tax_provider->setApiCredentials($this->api_credentials); + + $tax_data = $tax_provider->run(); + + $this->company->tax_data = $tax_data; + + $this->company->save(); + + return $this; + + + return $this; + + } + + private function configureProvider(?string $provider): self + { + + match($provider){ + ZipTax::class => $this->configureZipTax(), + default => $this->configureZipTax(), + }; + + return $this; + + } + + private function configureZipTax(): self + { + + $this->provider = ZipTax::class; + + $this->api_credentials = config('services.tax.zip_tax.key'); + + return $this; + + } } \ No newline at end of file diff --git a/config/services.php b/config/services.php index 78d3df8341d6..8cbdfdfe3d98 100644 --- a/config/services.php +++ b/config/services.php @@ -96,4 +96,10 @@ return [ 'redirect' => env('BITBUCKET_OAUTH_REDIRECT'), ], + 'tax' => [ + 'zip_tax' => [ + 'key' => env('ZIP_TAX_KEY', false), + ], + ] + ] ]; From d52d2f1f37ff1e55f724eb2824068f21624c7521 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 27 Mar 2023 07:46:26 +1100 Subject: [PATCH 10/38] Refactor for taxes --- app/DataMapper/Tax/RuleInterface.php | 9 + app/DataMapper/Tax/de/Rule.php | 26 ++- app/DataMapper/Tax/us/Rule.php | 28 ++- app/Helpers/Invoice/InvoiceItemSum.php | 4 +- app/Models/Product.php | 1 + .../{ProcessRule.php => Providers/EuTax.php} | 4 +- app/Services/Tax/Providers/TaxProvider.php | 194 ++++++++++++++++++ app/Services/Tax/TaxService.php | 93 +-------- .../{ProcessRuleTest.php => EuTaxTest.php} | 12 +- 9 files changed, 266 insertions(+), 105 deletions(-) rename app/Services/Tax/{ProcessRule.php => Providers/EuTax.php} (98%) create mode 100644 app/Services/Tax/Providers/TaxProvider.php rename tests/Unit/Tax/{ProcessRuleTest.php => EuTaxTest.php} (93%) diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index a9403451b539..633ba0b7be8e 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -11,6 +11,9 @@ namespace App\DataMapper\Tax; +use App\Models\Client; +use App\DataMapper\Tax\ZipTax\Response; + interface RuleInterface { public function tax(); @@ -27,5 +30,11 @@ interface RuleInterface public function taxPhysical(); + public function taxReduced(); + public function default(); + + public function setClient(Client $client); + + public function setTaxData(Response $tax_data); } \ No newline at end of file diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index 6097338b9d7e..55d47ef208cc 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -11,6 +11,7 @@ namespace App\DataMapper\Tax\de; +use App\Models\Client; use App\Models\Product; use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\ZipTax\Response; @@ -100,10 +101,24 @@ class Rule implements RuleInterface public string $tax_name3 = ''; public float $tax_rate3 = 0; + protected ?Client $client; - public function __construct(public Response $tax_data) + public function __construct() + { + } + + public function setClient(Client $client): self + { + $this->client = $client; + + return $this; + } + + public function setTaxData(Response $tax_data): self { $this->tax_data = $tax_data; + + return $this; } public function tax(): self @@ -126,12 +141,21 @@ class Rule implements RuleInterface Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), + Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), default => $this->default(), }; return $this; } + public function taxReduced(): self + { + $this->tax_rate1 = $this->vat_reduced_rate; + $this->tax_name1 = 'VAT'; + + return $this; + } + public function taxExempt(): self { $this->tax_name1 = ''; diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index 7fee2564dac3..7454f6b0ca3a 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -11,6 +11,7 @@ namespace App\DataMapper\Tax\us; +use App\Models\Client; use App\Models\Product; use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\ZipTax\Response; @@ -80,10 +81,26 @@ class Rule implements RuleInterface public string $tax_name3 = ''; public float $tax_rate3 = 0; - public function __construct(public Response $tax_data) + public ?Client $client; + + public ?Response $tax_data; + + public function __construct() + { + } + + public function setTaxData(Response $tax_data): self { $this->tax_data = $tax_data; - nlog($tax_data); + + return $this; + } + + public function setClient(Client $client):self + { + $this->client = $client; + + return $this; } public function tax(): self @@ -158,4 +175,11 @@ class Rule implements RuleInterface return $this; } + + public function taxReduced(): self + { + $this->tax(); + + return $this; + } } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index f093962f7f98..99de411c0be7 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -112,8 +112,8 @@ class InvoiceItemSum $tax_data = new Response($this->invoice->tax_data); - $this->rule = new $class($tax_data); - + $this->rule = new $class(); + $this->rule->setTaxData($tax_data); $this->calc_tax = true; return $this; diff --git a/app/Models/Product.php b/app/Models/Product.php index 30623b9c706b..ad17e87038fa 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -116,6 +116,7 @@ class Product extends BaseModel public const PRODUCT_TYPE_DIGITAL = 3; public const PRODUCT_TYPE_SHIPPING = 4; public const PRODUCT_TAX_EXEMPT = 5; + public const PRODUCT_TYPE_REDUCED_TAX = 6; protected $fillable = [ 'custom_value1', diff --git a/app/Services/Tax/ProcessRule.php b/app/Services/Tax/Providers/EuTax.php similarity index 98% rename from app/Services/Tax/ProcessRule.php rename to app/Services/Tax/Providers/EuTax.php index 21a4b197d799..9c29727d72b8 100644 --- a/app/Services/Tax/ProcessRule.php +++ b/app/Services/Tax/Providers/EuTax.php @@ -9,14 +9,14 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\Services\Tax; +namespace App\Services\Tax\Providers; use App\Models\Client; use App\Models\Company; use Illuminate\Support\Str; use App\DataMapper\Tax\de\Rule; use App\Services\Tax\VatNumberCheck; -class ProcessRule +class EuTax { public Rule $rule; diff --git a/app/Services/Tax/Providers/TaxProvider.php b/app/Services/Tax/Providers/TaxProvider.php new file mode 100644 index 000000000000..7690eca00475 --- /dev/null +++ b/app/Services/Tax/Providers/TaxProvider.php @@ -0,0 +1,194 @@ +configureProvider($this->provider); //hard coded for now to one provider, but we'll be able to swap these out later + + $company_details = [ + 'address1' => $this->company->settings->address1, + 'address2' => $this->company->settings->address2, + 'city' => $this->company->settings->city, + 'state' => $this->company->settings->state, + 'postal_code' => $this->company->settings->postal_code, + 'country_id' => $this->company->settings->country_id, + ]; + + $tax_provider = new $this->provider($company_details); + + $tax_provider->setApiCredentials($this->api_credentials); + + $tax_data = $tax_provider->run(); + + $this->company->tax_data = $tax_data; + + $this->company->save(); + + return $this; + + } + + public function updateClientTaxData(): self + { + $this->configureProvider($this->provider); //hard coded for now to one provider, but we'll be able to swap these out later + + $billing_details =[ + 'address1' => $this->client->address1, + 'address2' => $this->client->address2, + 'city' => $this->client->city, + 'state' => $this->client->state, + 'postal_code' => $this->client->postal_code, + 'country_id' => $this->client->country_id, + ]; + + $shipping_details =[ + 'address1' => $this->client->shipping_address1, + 'address2' => $this->client->shipping_address2, + 'city' => $this->client->shipping_city, + 'state' => $this->client->shipping_state, + 'postal_code' => $this->client->shipping_postal_code, + 'country_id' => $this->client->shipping_country_id, + ]; + + + $tax_provider = new $this->provider(); + + $tax_provider->setApiCredentials($this->api_credentials); + + $tax_data = $tax_provider->run(); + + $this->company->tax_data = $tax_data; + + $this->company->save(); + + return $this; + + + return $this; + + } + + private function configureProvider(?string $provider): self + { + + match($this->client->country->iso_3166_2){ + 'US' => $this->configureZipTax(), + "AT" => $this->configureEuTax(), + "BE" => $this->configureEuTax(), + "BG" => $this->configureEuTax(), + "HR" => $this->configureEuTax(), + "CY" => $this->configureEuTax(), + "CZ" => $this->configureEuTax(), + "DK" => $this->configureEuTax(), + "EE" => $this->configureEuTax(), + "FI" => $this->configureEuTax(), + "FR" => $this->configureEuTax(), + "DE" => $this->configureEuTax(), + "GR" => $this->configureEuTax(), + "HU" => $this->configureEuTax(), + "IE" => $this->configureEuTax(), + "IT" => $this->configureEuTax(), + "LV" => $this->configureEuTax(), + "LT" => $this->configureEuTax(), + "LU" => $this->configureEuTax(), + "MT" => $this->configureEuTax(), + "NL" => $this->configureEuTax(), + "PL" => $this->configureEuTax(), + "PT" => $this->configureEuTax(), + "RO" => $this->configureEuTax(), + "SK" => $this->configureEuTax(), + "SI" => $this->configureEuTax(), + "ES" => $this->configureEuTax(), + "SE" => $this->configureEuTax(), + default => $this->noTaxRegionDefined(), + }; + + return $this; + + } + + private function configureEuTax(): self + { + $this->provider = EuTax::class; + + // $this->api_credentials = config('services.tax.eu_tax.key'); + + return $this; + } + + private function noTaxRegionDefined(): self + { + return $this; + } + + private function configureZipTax(): self + { + + $this->provider = ZipTax::class; + + $this->api_credentials = config('services.tax.zip_tax.key'); + + return $this; + + } + +} \ No newline at end of file diff --git a/app/Services/Tax/TaxService.php b/app/Services/Tax/TaxService.php index 20ef2f042013..678418c240b5 100644 --- a/app/Services/Tax/TaxService.php +++ b/app/Services/Tax/TaxService.php @@ -18,101 +18,10 @@ use App\Services\Tax\Providers\ZipTax; class TaxService { - private string $provider = ZipTax::class; - - private mixed $api_credentials; public function __construct(protected Company $company, protected Client $client) { } - public function updateCompanyTaxData(): self - { - $this->configureProvider($this->provider); //hard coded for now to one provider, but we'll be able to swap these out later - - $company_details = [ - 'address1' => $this->company->settings->address1, - 'address2' => $this->company->settings->address2, - 'city' => $this->company->settings->city, - 'state' => $this->company->settings->state, - 'postal_code' => $this->company->settings->postal_code, - 'country_id' => $this->company->settings->country_id, - ]; - - $tax_provider = new $this->provider($company_details); - - $tax_provider->setApiCredentials($this->api_credentials); - - $tax_data = $tax_provider->run(); - - $this->company->tax_data = $tax_data; - - $this->company->save(); - - return $this; - - } - - public function updateClientTaxData(): self - { - $this->configureProvider($this->provider); //hard coded for now to one provider, but we'll be able to swap these out later - - $billing_details =[ - 'address1' => $this->client->address1, - 'address2' => $this->client->address2, - 'city' => $this->client->city, - 'state' => $this->client->state, - 'postal_code' => $this->client->postal_code, - 'country_id' => $this->client->country_id, - ]; - - $shipping_details =[ - 'address1' => $this->client->shipping_address1, - 'address2' => $this->client->shipping_address2, - 'city' => $this->client->shipping_city, - 'state' => $this->client->shipping_state, - 'postal_code' => $this->client->shipping_postal_code, - 'country_id' => $this->client->shipping_country_id, - ]; - - - $tax_provider = new $this->provider(); - - $tax_provider->setApiCredentials($this->api_credentials); - - $tax_data = $tax_provider->run(); - - $this->company->tax_data = $tax_data; - - $this->company->save(); - - return $this; - - - return $this; - - } - - private function configureProvider(?string $provider): self - { - - match($provider){ - ZipTax::class => $this->configureZipTax(), - default => $this->configureZipTax(), - }; - - return $this; - - } - - private function configureZipTax(): self - { - - $this->provider = ZipTax::class; - - $this->api_credentials = config('services.tax.zip_tax.key'); - - return $this; - - } + } \ No newline at end of file diff --git a/tests/Unit/Tax/ProcessRuleTest.php b/tests/Unit/Tax/EuTaxTest.php similarity index 93% rename from tests/Unit/Tax/ProcessRuleTest.php rename to tests/Unit/Tax/EuTaxTest.php index f8c00ce48bb0..4db56aad1cff 100644 --- a/tests/Unit/Tax/ProcessRuleTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -16,15 +16,15 @@ use App\Models\Client; use App\Models\Company; use Tests\MockAccountData; use App\DataMapper\Tax\de\Rule; -use App\Services\Tax\ProcessRule; +use App\Services\Tax\Providers\EuTax; use App\DataMapper\CompanySettings; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; /** - * @test App\Services\Tax\ProcessRule + * @test App\Services\Tax\Providers\EuTax */ -class ProcessRuleTest extends TestCase +class EuTaxTest extends TestCase { use MockAccountData; use DatabaseTransactions; @@ -60,7 +60,7 @@ class ProcessRuleTest extends TestCase 'shipping_country_id' => 276, ]); - $process = new ProcessRule($company, $client); + $process = new EuTax($company, $client); $process->run(); $this->assertEquals('de', $process->getVendorCountryCode()); @@ -96,7 +96,7 @@ class ProcessRuleTest extends TestCase 'shipping_country_id' => 56, ]); - $process = new ProcessRule($company, $client); + $process = new EuTax($company, $client); $process->run(); $this->assertEquals('de', $process->getVendorCountryCode()); @@ -132,7 +132,7 @@ class ProcessRuleTest extends TestCase 'shipping_country_id' => 840, ]); - $process = new ProcessRule($company, $client); + $process = new EuTax($company, $client); $process->run(); $this->assertEquals('de', $process->getVendorCountryCode()); From b94743f42de601a8999cd6d72988cdf53566ded1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 27 Mar 2023 07:57:29 +1100 Subject: [PATCH 11/38] Refactor for taxes --- app/DataMapper/Tax/de/Rule.php | 11 ++++++++ app/DataMapper/Tax/us/Rule.php | 8 ++++++ app/Helpers/Invoice/InvoiceItemSum.php | 37 ++++++++++++++++++++++++-- 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index 55d47ef208cc..ee995c724de3 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -103,6 +103,8 @@ class Rule implements RuleInterface protected ?Client $client; + protected ?Response $tax_data; + public function __construct() { } @@ -123,6 +125,10 @@ class Rule implements RuleInterface public function tax(): self { + if($this->client->is_tax_exempt) + return $this->taxExempt(); + + $this->tax_name1 = $this->vat_rate; $this->tax_rate1 = "VAT"; @@ -132,6 +138,11 @@ class Rule implements RuleInterface public function taxByType(?int $product_tax_type): self { + + if ($this->client->is_tax_exempt) { + return $this->taxExempt(); + } + if(!$product_tax_type) return $this; diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index 7454f6b0ca3a..370ac0c3da82 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -105,6 +105,9 @@ class Rule implements RuleInterface public function tax(): self { + if($this->client->is_tax_exempt) + return $this->taxExempt(); + $this->tax_rate1 = $this->tax_data->taxSales * 100; $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; @@ -117,6 +120,11 @@ class Rule implements RuleInterface if(!$product_tax_type) return $this; + + if ($this->client->is_tax_exempt) { + return $this->taxExempt(); + } + match($product_tax_type){ Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 99de411c0be7..b35ad245d211 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -25,6 +25,38 @@ class InvoiceItemSum use Discounter; use Taxer; + private array $tax_jurisdictions = [ + 'AT', // Austria + 'BE', // Belgium + 'BG', // Bulgaria + 'CY', // Cyprus + 'CZ', // Czech Republic + 'DE', // Germany + 'DK', // Denmark + 'EE', // Estonia + 'ES', // Spain + 'FI', // Finland + 'FR', // France + 'GR', // Greece + 'HR', // Croatia + 'HU', // Hungary + 'IE', // Ireland + 'IT', // Italy + 'LT', // Lithuania + 'LU', // Luxembourg + 'LV', // Latvia + 'MT', // Malta + 'NL', // Netherlands + 'PL', // Poland + 'PT', // Portugal + 'RO', // Romania + 'SE', // Sweden + 'SI', // Slovenia + 'SK', // Slovakia + + 'US', //USA + ]; + protected $invoice; private $items; @@ -102,18 +134,19 @@ class InvoiceItemSum private function shouldCalculateTax(): self { - if (!$this->invoice->company->calculate_taxes || $this->client->is_tax_exempt) { + if (!$this->invoice->company->calculate_taxes) { $this->calc_tax = false; return $this; } - if (in_array($this->client->country->iso_3166_2, ['US'])) { //only calculate for USA + if (in_array($this->client->country->iso_3166_2, $this->tax_jurisdictions)) { //only calculate for supported tax jurisdictions $class = "App\DataMapper\Tax\\".strtolower($this->client->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->calc_tax = true; return $this; From 160ca1c9ad557e9caaa329541efdb6a35d4bef30 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 27 Mar 2023 14:47:01 +1100 Subject: [PATCH 12/38] Refactor for taxes --- app/DataMapper/Tax/de/Rule.php | 45 +++++++++++++++---- app/Helpers/Invoice/InvoiceItemSum.php | 3 +- app/Models/Account.php | 4 ++ app/Models/BankAccount.php | 1 + app/Models/BankIntegration.php | 1 + app/Models/Client.php | 17 +++++++ app/Models/ClientContact.php | 5 +++ app/Models/Company.php | 44 ++++++++++++++++++ app/Models/CompanyGateway.php | 1 + app/Models/CompanyUser.php | 3 ++ app/Models/Credit.php | 7 +++ app/Models/Expense.php | 1 + app/Models/GatewayType.php | 1 + app/Models/GroupSetting.php | 2 + app/Models/Invoice.php | 9 ++++ app/Models/Payment.php | 5 +++ app/Models/Product.php | 1 + app/Models/Project.php | 2 + app/Models/Proposal.php | 1 + app/Models/PurchaseOrder.php | 6 +++ app/Models/Quote.php | 4 ++ app/Models/RecurringExpense.php | 1 + app/Models/RecurringInvoice.php | 5 +++ app/Models/RecurringQuote.php | 5 +++ app/Models/Task.php | 1 + app/Models/User.php | 7 +++ app/Models/Vendor.php | 4 ++ app/Models/VendorContact.php | 2 + app/Services/Tax/Providers/TaxProvider.php | 11 ++--- app/Transformers/ClientTransformer.php | 2 + config/services.php | 3 +- ...054758_add_client_is_exempt_from_taxes.php | 1 + 32 files changed, 186 insertions(+), 19 deletions(-) diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index ee995c724de3..134063121979 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -22,6 +22,37 @@ class Rule implements RuleInterface public float $vat_threshold = 10000; + + public array $distance_selling_thresholds = [ + "AT" => 35000, + "BE" => 35000, + "BG" => 35800, + "HR" => 35900, + "CY" => 0, // Cyprus does not have a distance selling threshold, so for cyprus buyers and sellers always use this rate + "CZ" => 44200, + "DK" => 37500, + "EE" => 35000, + "FI" => 35000, + "FR" => 35000, + "DE" => 100000, + "GR" => 35000, + "HU" => 25000, + "IE" => 35000, + "IT" => 35000, + "LV" => 35000, + "LT" => 35000, + "LU" => 100000, + "MT" => 35000, + "NL" => 100000, + "PL" => 36900, + "PT" => 35000, + "RO" => 24200, + "SK" => 35000, + "SI" => 35000, + "ES" => 35000, + "SE" => 31700 + ]; + public float $vat_reduced_rate = 7; public float $vat_reduced_threshold = 10000; @@ -123,14 +154,14 @@ class Rule implements RuleInterface return $this; } + //need to add logic here to capture if public function tax(): self { - if($this->client->is_tax_exempt) + if($this->client->is_tax_exempt || $this->client->has_valid_vat_number) return $this->taxExempt(); - $this->tax_name1 = $this->vat_rate; - $this->tax_rate1 = "VAT"; + $this->tax_rate1 = "MwSt."; return $this; @@ -162,7 +193,7 @@ class Rule implements RuleInterface public function taxReduced(): self { $this->tax_rate1 = $this->vat_reduced_rate; - $this->tax_name1 = 'VAT'; + $this->tax_name1 = 'ermäßigte MwSt.'; return $this; } @@ -184,16 +215,14 @@ class Rule implements RuleInterface public function taxService(): self { - if($this->tax_data->txbService == 'Y') - $this->tax(); + $this->tax(); return $this; } public function taxShipping(): self { - if($this->tax_data->txbFreight == 'Y') - $this->tax(); + $this->tax(); return $this; } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index b35ad245d211..6e859b2de5a9 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -138,7 +138,8 @@ class InvoiceItemSum $this->calc_tax = false; return $this; } - + + //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\\".strtolower($this->client->country->iso_3166_2)."\\Rule"; diff --git a/app/Models/Account.php b/app/Models/Account.php index b213e0b3b094..3388c4ad4c3b 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -146,6 +146,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $companies * @property-read \Illuminate\Database\Eloquent\Collection $company_users * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class Account extends BaseModel diff --git a/app/Models/BankAccount.php b/app/Models/BankAccount.php index aa285cc7c0b5..48b0c78b462f 100644 --- a/app/Models/BankAccount.php +++ b/app/Models/BankAccount.php @@ -36,6 +36,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts + * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @mixin \Eloquent */ class BankAccount extends BaseModel diff --git a/app/Models/BankIntegration.php b/app/Models/BankIntegration.php index abcdd5e87bb4..d9fb837ee06f 100644 --- a/app/Models/BankIntegration.php +++ b/app/Models/BankIntegration.php @@ -79,6 +79,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions + * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @mixin \Eloquent */ class BankIntegration extends BaseModel diff --git a/app/Models/Client.php b/app/Models/Client.php index 4c29cb728a74..8da22191e69f 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -240,6 +240,23 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $system_logs * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @method static \Illuminate\Database\Eloquent\Builder|Client whereIsTaxExempt($value) + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Client extends BaseModel implements HasLocalePreference diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 72d71c689a60..19f3b6728c73 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -143,6 +143,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations * @mixin \Eloquent */ class ClientContact extends Authenticatable implements HasLocalePreference diff --git a/app/Models/Company.php b/app/Models/Company.php index ea08cc9a63f8..9719f26a95f9 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -464,6 +464,50 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read \Illuminate\Database\Eloquent\Collection $vendors * @property-read \Illuminate\Database\Eloquent\Collection $webhooks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_documents + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transaction_rules + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transactions + * @property-read \Illuminate\Database\Eloquent\Collection $client_contacts + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $company_gateways + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $designs + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expense_categories + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $group_settings + * @property-read \Illuminate\Database\Eloquent\Collection $groups + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $products + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_orders + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $system_log_relation + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $task_schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $task_statuses + * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $tax_rates + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $tokens_hashed + * @property-read \Illuminate\Database\Eloquent\Collection $user_designs + * @property-read \Illuminate\Database\Eloquent\Collection $user_payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $vendors + * @property-read \Illuminate\Database\Eloquent\Collection $webhooks * @mixin \Eloquent */ class Company extends BaseModel diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index af59605cdb88..1b69207ac9e2 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -96,6 +96,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @mixin \Eloquent */ class CompanyGateway extends BaseModel diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index d53859d776a7..510093daec2b 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -79,6 +79,9 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $token * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $token + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class CompanyUser extends Pivot diff --git a/app/Models/Credit.php b/app/Models/Credit.php index c5872adc5a48..e42dd8b9d80a 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -213,6 +213,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class Credit extends BaseModel diff --git a/app/Models/Expense.php b/app/Models/Expense.php index a1d64852d2c4..27e1bcb12a74 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -134,6 +134,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Expense extends BaseModel diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 5363a62c0929..8e4dc24489df 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -32,6 +32,7 @@ namespace App\Models; * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods + * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel diff --git a/app/Models/GroupSetting.php b/app/Models/GroupSetting.php index c71932c91a5f..637d8978e35d 100644 --- a/app/Models/GroupSetting.php +++ b/app/Models/GroupSetting.php @@ -62,6 +62,8 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $clients * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class GroupSetting extends StaticModel diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 4125d794aece..8b12ee6003e7 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -241,6 +241,15 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Invoice extends BaseModel diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 48f6a9abd0ba..de69ef695fe1 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -152,6 +152,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $paymentables + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $paymentables * @mixin \Eloquent */ class Payment extends BaseModel diff --git a/app/Models/Product.php b/app/Models/Product.php index ad17e87038fa..117178af997b 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -102,6 +102,7 @@ use League\CommonMark\CommonMarkConverter; * @method static \Illuminate\Database\Eloquent\Builder|Product whereTaxId($value) * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Product extends BaseModel diff --git a/app/Models/Project.php b/app/Models/Project.php index 5cd470447495..c8690fd0aa6c 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -80,6 +80,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Project extends BaseModel diff --git a/app/Models/Proposal.php b/app/Models/Proposal.php index 8cc3cb023912..5c79ce24d415 100644 --- a/app/Models/Proposal.php +++ b/app/Models/Proposal.php @@ -32,6 +32,7 @@ use App\Utils\Traits\MakesHash; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Proposal extends BaseModel diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index a26d07be9fa9..32ec9d325945 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -204,6 +204,12 @@ use Illuminate\Support\Facades\Storage; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class PurchaseOrder extends BaseModel diff --git a/app/Models/Quote.php b/app/Models/Quote.php index 96ae34f00539..d263490a0836 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -197,6 +197,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @mixin \Eloquent */ class Quote extends BaseModel diff --git a/app/Models/RecurringExpense.php b/app/Models/RecurringExpense.php index ec8bdf5d4e44..02fad06b4f11 100644 --- a/app/Models/RecurringExpense.php +++ b/app/Models/RecurringExpense.php @@ -143,6 +143,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class RecurringExpense extends BaseModel diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 98741f9cb072..3c0d1a533283 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -199,6 +199,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @mixin \Eloquent */ class RecurringInvoice extends BaseModel diff --git a/app/Models/RecurringQuote.php b/app/Models/RecurringQuote.php index 74d8f905d2cf..025ff3b22b3c 100644 --- a/app/Models/RecurringQuote.php +++ b/app/Models/RecurringQuote.php @@ -191,6 +191,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $quotes * @mixin \Eloquent */ class RecurringQuote extends BaseModel diff --git a/app/Models/Task.php b/app/Models/Task.php index 30bbaa98e1ed..6f17f5c2d272 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -95,6 +95,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Task extends BaseModel diff --git a/app/Models/User.php b/app/Models/User.php index 1ec59ba1325a..96661a9779e0 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -167,6 +167,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @mixin \Eloquent */ class User extends Authenticatable implements MustVerifyEmail diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 4b5ddc137618..b169d3efb72d 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -120,6 +120,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $contacts * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact * @mixin \Eloquent */ class Vendor extends BaseModel diff --git a/app/Models/VendorContact.php b/app/Models/VendorContact.php index 4d3b0a83b126..cc2110929c9b 100644 --- a/app/Models/VendorContact.php +++ b/app/Models/VendorContact.php @@ -118,6 +118,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @mixin \Eloquent */ class VendorContact extends Authenticatable implements HasLocalePreference diff --git a/app/Services/Tax/Providers/TaxProvider.php b/app/Services/Tax/Providers/TaxProvider.php index 7690eca00475..4d71522cf617 100644 --- a/app/Services/Tax/Providers/TaxProvider.php +++ b/app/Services/Tax/Providers/TaxProvider.php @@ -13,8 +13,7 @@ namespace App\Services\Tax\Providers; use App\Models\Client; use App\Models\Company; -use Illuminate\Http\Client\Response; -use Illuminate\Support\Facades\Http; +use App\Services\Tax\Providers\EuTax; class TaxProvider { @@ -53,7 +52,6 @@ class TaxProvider private mixed $api_credentials; - public function __construct(protected Company $company, protected Client $client) { @@ -122,9 +120,6 @@ class TaxProvider return $this; - - return $this; - } private function configureProvider(?string $provider): self @@ -170,13 +165,13 @@ class TaxProvider { $this->provider = EuTax::class; - // $this->api_credentials = config('services.tax.eu_tax.key'); - return $this; } private function noTaxRegionDefined(): self { + throw new \Exception("No tax region defined for this country"); + return $this; } diff --git a/app/Transformers/ClientTransformer.php b/app/Transformers/ClientTransformer.php index 959e8f751985..c3726e467d8f 100644 --- a/app/Transformers/ClientTransformer.php +++ b/app/Transformers/ClientTransformer.php @@ -147,6 +147,8 @@ class ClientTransformer extends EntityTransformer 'created_at' => (int) $client->created_at, 'display_name' => $client->present()->name(), 'number' => (string) $client->number ?: '', + 'has_valid_vat_number' => (bool) $client->has_valid_vat_number, + 'is_tax_exempt' => (bool) $client->is_tax_exempt, ]; } } diff --git a/config/services.php b/config/services.php index 8cbdfdfe3d98..e252ef9a4b27 100644 --- a/config/services.php +++ b/config/services.php @@ -100,6 +100,5 @@ return [ 'zip_tax' => [ 'key' => env('ZIP_TAX_KEY', false), ], - ] - ] + ], ]; diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index 44ba1fbc23a0..d4bd2a416061 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -15,6 +15,7 @@ return new class extends Migration { Schema::table('clients', function (Blueprint $table) { $table->boolean('is_tax_exempt')->default(false); + $table->boolean('has_valid_vat_number')->default(false); }); } From 67179887d788bdf252f11d592a86dc70ea880500 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 28 Mar 2023 07:47:07 +1100 Subject: [PATCH 13/38] Update BaseRule taxes --- app/DataMapper/Tax/BaseRule.php | 268 ++++++++++++++++++++++++++++++++ app/DataMapper/Tax/de/Rule.php | 91 +---------- app/DataMapper/Tax/us/Rule.php | 53 ------- app/Models/Account.php | 4 + app/Models/BankAccount.php | 1 + app/Models/BankIntegration.php | 1 + app/Models/Client.php | 17 ++ app/Models/ClientContact.php | 5 + app/Models/Company.php | 44 ++++++ app/Models/CompanyGateway.php | 1 + app/Models/CompanyUser.php | 3 + app/Models/Credit.php | 7 + app/Models/Expense.php | 1 + app/Models/GatewayType.php | 1 + app/Models/GroupSetting.php | 2 + app/Models/Invoice.php | 9 ++ app/Models/Payment.php | 5 + app/Models/Product.php | 1 + app/Models/Project.php | 2 + app/Models/Proposal.php | 1 + app/Models/PurchaseOrder.php | 6 + app/Models/Quote.php | 4 + app/Models/RecurringExpense.php | 1 + app/Models/RecurringInvoice.php | 5 + app/Models/RecurringQuote.php | 5 + app/Models/Task.php | 1 + app/Models/User.php | 7 + app/Models/Vendor.php | 4 + app/Models/VendorContact.php | 2 + 29 files changed, 411 insertions(+), 141 deletions(-) create mode 100644 app/DataMapper/Tax/BaseRule.php diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php new file mode 100644 index 000000000000..979ab3f07b74 --- /dev/null +++ b/app/DataMapper/Tax/BaseRule.php @@ -0,0 +1,268 @@ + 35000, + "BE" => 35000, + "BG" => 35800, + "HR" => 35900, + "CY" => 0, // Cyprus does not have a distance selling threshold, so for cyprus buyers and sellers always use this rate + "CZ" => 44200, + "DK" => 37500, + "EE" => 35000, + "FI" => 35000, + "FR" => 35000, + "DE" => 100000, + "GR" => 35000, + "HU" => 25000, + "IE" => 35000, + "IT" => 35000, + "LV" => 35000, + "LT" => 35000, + "LU" => 100000, + "MT" => 35000, + "NL" => 100000, + "PL" => 36900, + "PT" => 35000, + "RO" => 24200, + "SK" => 35000, + "SI" => 35000, + "ES" => 35000, + "SE" => 31700 + ]; + + public float $vat_rate = 19; + public float $vat_threshold = 10000; + public float $vat_reduced_rate = 7; + public float $vat_reduced_threshold = 10000; + public float $at_vat_rate = 20; // Austria + public float $be_vat_rate = 21; // Belgium + public float $bg_vat_rate = 20; // Bulgaria + public float $hr_vat_rate = 25; // Croatia + public float $cy_vat_rate = 19; // Cyprus + public float $cz_vat_rate = 21; // Czech Republic + public float $dk_vat_rate = 25; // Denmark + public float $ee_vat_rate = 20; // Estonia + public float $fi_vat_rate = 24; // Finland + public float $fr_vat_rate = 20; // France + public float $de_vat_rate = 19; // Germany + public float $gr_vat_rate = 24; // Greece + public float $hu_vat_rate = 27; // Hungary + public float $ie_vat_rate = 23; // Ireland + public float $it_vat_rate = 22; // Italy + public float $lv_vat_rate = 21; // Latvia + public float $lt_vat_rate = 21; // Lithuania + public float $lu_vat_rate = 17; // Luxembourg + public float $mt_vat_rate = 18; // Malta + public float $nl_vat_rate = 21; // Netherlands + public float $pl_vat_rate = 23; // Poland + public float $pt_vat_rate = 23; // Portugal + public float $ro_vat_rate = 19; // Romania + public float $sk_vat_rate = 20; // Slovakia + public float $si_vat_rate = 22; // Slovenia + public float $es_vat_rate = 21; // Spain + public float $se_vat_rate = 25; // Sweden + public float $gb_vat_rate = 20; // United Kingdom + public bool $consumer_tax_exempt = false; + public bool $business_tax_exempt = true; + public bool $eu_business_tax_exempt = true; + public bool $foreign_business_tax_exempt = true; + public bool $foreign_consumer_tax_exempt = true; +/** EU TAXES */ + + +/** US TAXES */ + public float $al_sales_tax_rate = 4; // Alabama + public float $ak_sales_tax_rate = 0; // Alaska + public float $az_sales_tax_rate = 5.6; // Arizona + public float $ar_sales_tax_rate = 6.5; // Arkansas + public float $ca_sales_tax_rate = 7.25; // California - https://services.maps.cdtfa.ca.gov/api/taxrate/GetRateByAddress?address=2444+s+alameda+st&city=los+angeles&zip=90058 + public float $co_sales_tax_rate = 2.9; // Colorado + public float $ct_sales_tax_rate = 6.35; // Connecticut + public float $de_sales_tax_rate = 0; // Delaware + public float $fl_sales_tax_rate = 6; // Florida + public float $ga_sales_tax_rate = 4; // Georgia + public float $hi_sales_tax_rate = 4; // Hawaii + public float $id_sales_tax_rate = 6; // Idaho + public float $il_sales_tax_rate = 6.25; // Illinois + public float $in_sales_tax_rate = 7; // Indiana + public float $ia_sales_tax_rate = 6; // Iowa + public float $ks_sales_tax_rate = 6.5; // Kansas + public float $ky_sales_tax_rate = 6; // Kentucky + public float $la_sales_tax_rate = 4.45; // Louisiana + public float $me_sales_tax_rate = 5.5; // Maine + public float $md_sales_tax_rate = 6; // Maryland + public float $ma_sales_tax_rate = 6.25; // Massachusetts + public float $mi_sales_tax_rate = 6; // Michigan + public float $mn_sales_tax_rate = 6.875; // Minnesota + public float $ms_sales_tax_rate = 7; // Mississippi + public float $mo_sales_tax_rate = 4.225; // Missouri + public float $mt_sales_tax_rate = 0; // Montana + public float $ne_sales_tax_rate = 5.5; // Nebraska + public float $nv_sales_tax_rate = 6.85; // Nevada + public float $nh_sales_tax_rate = 0; // New Hampshire + public float $nj_sales_tax_rate = 6.625; // New Jersey + public float $nm_sales_tax_rate = 5.125; // New Mexico + public float $ny_sales_tax_rate = 4; // New York + public float $nc_sales_tax_rate = 4.75; // North Carolina + public float $nd_sales_tax_rate = 5; // North Dakota + public float $oh_sales_tax_rate = 5.75; // Ohio + public float $ok_sales_tax_rate = 4.5; // Oklahoma + public float $or_sales_tax_rate = 0; // Oregon + public float $pa_sales_tax_rate = 6; // Pennsylvania + public float $ri_sales_tax_rate = 7; // Rhode Island + public float $sc_sales_tax_rate = 6; // South Carolina + public float $sd_sales_tax_rate = 4.5; // South Dakota + public float $tn_sales_tax_rate = 7; // Tennessee + public float $tx_sales_tax_rate = 6.25; // Texas + public float $ut_sales_tax_rate = 4.7; // Utah + public float $vt_sales_tax_rate = 6; // Vermont + public float $va_sales_tax_rate = 5.3; // Virginia + public float $wa_sales_tax_rate = 6.5; // Washington + public float $wv_sales_tax_rate = 6; // West Virginia + public float $wi_sales_tax_rate = 5; // Wisconsin + public float $wy_sales_tax_rate = 4; // Wyoming + public float $dc_sales_tax_rate = 6; // District of Columbia + public float $pr_sales_tax_rate = 11.5; // Puerto Rico +/**US TAXES */ + + + public string $tax_name1 = ''; + public float $tax_rate1 = 0; + + public string $tax_name2 = ''; + public float $tax_rate2 = 0; + + public string $tax_name3 = ''; + public float $tax_rate3 = 0; + + protected ?Client $client; + + protected ?Response $tax_data; + + public function __construct() + { + } + + public function setClient(Client $client): self + { + $this->client = $client; + + return $this; + } + + public function setTaxData(Response $tax_data): self + { + $this->tax_data = $tax_data; + + return $this; + } + + //need to add logic here to capture if + public function tax(): self + { + if ($this->client->is_tax_exempt || $this->client->has_valid_vat_number) { + return $this->taxExempt(); + } + + $this->tax_name1 = $this->vat_rate; + $this->tax_rate1 = "VAT"; + + return $this; + } + + public function taxByType(?int $product_tax_type): self + { + if ($this->client->is_tax_exempt) { + return $this->taxExempt(); + } + + if (!$product_tax_type) { + return $this; + } + + match ($product_tax_type) { + Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), + Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), + Product::PRODUCT_TYPE_SERVICE => $this->taxService(), + Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), + Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), + Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), + default => $this->default(), + }; + + return $this; + } + + public function taxReduced(): self + { + $this->tax_rate1 = $this->vat_reduced_rate; + $this->tax_name1 = 'Reduced VAT'; + + return $this; + } + + public function taxExempt(): self + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + + return $this; + } + + public function taxDigital(): self + { + $this->tax(); + + return $this; + } + + public function taxService(): self + { + $this->tax(); + + return $this; + } + + public function taxShipping(): self + { + $this->tax(); + + return $this; + } + + public function taxPhysical(): self + { + $this->tax(); + + return $this; + } + + public function default(): self + { + $this->tax_name1 = ''; + $this->tax_rate1 = 0; + + return $this; + } +} diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index 134063121979..1717a46eed77 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -13,106 +13,21 @@ namespace App\DataMapper\Tax\de; use App\Models\Client; use App\Models\Product; +use App\DataMapper\Tax\BaseRule; use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\ZipTax\Response; -class Rule implements RuleInterface +class Rule extends BaseRule implements RuleInterface { + public float $vat_rate = 19; public float $vat_threshold = 10000; - - public array $distance_selling_thresholds = [ - "AT" => 35000, - "BE" => 35000, - "BG" => 35800, - "HR" => 35900, - "CY" => 0, // Cyprus does not have a distance selling threshold, so for cyprus buyers and sellers always use this rate - "CZ" => 44200, - "DK" => 37500, - "EE" => 35000, - "FI" => 35000, - "FR" => 35000, - "DE" => 100000, - "GR" => 35000, - "HU" => 25000, - "IE" => 35000, - "IT" => 35000, - "LV" => 35000, - "LT" => 35000, - "LU" => 100000, - "MT" => 35000, - "NL" => 100000, - "PL" => 36900, - "PT" => 35000, - "RO" => 24200, - "SK" => 35000, - "SI" => 35000, - "ES" => 35000, - "SE" => 31700 - ]; - public float $vat_reduced_rate = 7; public float $vat_reduced_threshold = 10000; - public float $at_vat_rate = 20; // Austria - - public float $be_vat_rate = 21; // Belgium - - public float $bg_vat_rate = 20; // Bulgaria - - public float $hr_vat_rate = 25; // Croatia - - public float $cy_vat_rate = 19; // Cyprus - - public float $cz_vat_rate = 21; // Czech Republic - - public float $dk_vat_rate = 25; // Denmark - - public float $ee_vat_rate = 20; // Estonia - - public float $fi_vat_rate = 24; // Finland - - public float $fr_vat_rate = 20; // France - - public float $de_vat_rate = 19; // Germany - - public float $gr_vat_rate = 24; // Greece - - public float $hu_vat_rate = 27; // Hungary - - public float $ie_vat_rate = 23; // Ireland - - public float $it_vat_rate = 22; // Italy - - public float $lv_vat_rate = 21; // Latvia - - public float $lt_vat_rate = 21; // Lithuania - - public float $lu_vat_rate = 17; // Luxembourg - - public float $mt_vat_rate = 18; // Malta - - public float $nl_vat_rate = 21; // Netherlands - - public float $pl_vat_rate = 23; // Poland - - public float $pt_vat_rate = 23; // Portugal - - public float $ro_vat_rate = 19; // Romania - - public float $sk_vat_rate = 20; // Slovakia - - public float $si_vat_rate = 22; // Slovenia - - public float $es_vat_rate = 21; // Spain - - public float $se_vat_rate = 25; // Sweden - - public float $gb_vat_rate = 20; // United Kingdom - public bool $consumer_tax_exempt = false; public bool $business_tax_exempt = true; diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index 370ac0c3da82..bc90457d5495 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -19,59 +19,6 @@ use App\DataMapper\Tax\ZipTax\Response; class Rule implements RuleInterface { - public float $al_sales_tax_rate = 4; // Alabama - public float $ak_sales_tax_rate = 0; // Alaska - public float $az_sales_tax_rate = 5.6; // Arizona - public float $ar_sales_tax_rate = 6.5; // Arkansas - public float $ca_sales_tax_rate = 7.25; // California - https://services.maps.cdtfa.ca.gov/api/taxrate/GetRateByAddress?address=2444+s+alameda+st&city=los+angeles&zip=90058 - public float $co_sales_tax_rate = 2.9; // Colorado - public float $ct_sales_tax_rate = 6.35; // Connecticut - public float $de_sales_tax_rate = 0; // Delaware - public float $fl_sales_tax_rate = 6; // Florida - public float $ga_sales_tax_rate = 4; // Georgia - public float $hi_sales_tax_rate = 4; // Hawaii - public float $id_sales_tax_rate = 6; // Idaho - public float $il_sales_tax_rate = 6.25; // Illinois - public float $in_sales_tax_rate = 7; // Indiana - public float $ia_sales_tax_rate = 6; // Iowa - public float $ks_sales_tax_rate = 6.5; // Kansas - public float $ky_sales_tax_rate = 6; // Kentucky - public float $la_sales_tax_rate = 4.45; // Louisiana - public float $me_sales_tax_rate = 5.5; // Maine - public float $md_sales_tax_rate = 6; // Maryland - public float $ma_sales_tax_rate = 6.25; // Massachusetts - public float $mi_sales_tax_rate = 6; // Michigan - public float $mn_sales_tax_rate = 6.875; // Minnesota - public float $ms_sales_tax_rate = 7; // Mississippi - public float $mo_sales_tax_rate = 4.225; // Missouri - public float $mt_sales_tax_rate = 0; // Montana - public float $ne_sales_tax_rate = 5.5; // Nebraska - public float $nv_sales_tax_rate = 6.85; // Nevada - public float $nh_sales_tax_rate = 0; // New Hampshire - public float $nj_sales_tax_rate = 6.625; // New Jersey - public float $nm_sales_tax_rate = 5.125; // New Mexico - public float $ny_sales_tax_rate = 4; // New York - public float $nc_sales_tax_rate = 4.75; // North Carolina - public float $nd_sales_tax_rate = 5; // North Dakota - public float $oh_sales_tax_rate = 5.75; // Ohio - public float $ok_sales_tax_rate = 4.5; // Oklahoma - public float $or_sales_tax_rate = 0; // Oregon - public float $pa_sales_tax_rate = 6; // Pennsylvania - public float $ri_sales_tax_rate = 7; // Rhode Island - public float $sc_sales_tax_rate = 6; // South Carolina - public float $sd_sales_tax_rate = 4.5; // South Dakota - public float $tn_sales_tax_rate = 7; // Tennessee - public float $tx_sales_tax_rate = 6.25; // Texas - public float $ut_sales_tax_rate = 4.7; // Utah - public float $vt_sales_tax_rate = 6; // Vermont - public float $va_sales_tax_rate = 5.3; // Virginia - public float $wa_sales_tax_rate = 6.5; // Washington - public float $wv_sales_tax_rate = 6; // West Virginia - public float $wi_sales_tax_rate = 5; // Wisconsin - public float $wy_sales_tax_rate = 4; // Wyoming - public float $dc_sales_tax_rate = 6; // District of Columbia - public float $pr_sales_tax_rate = 11.5; // Puerto Rico - public string $tax_name1 = ''; public float $tax_rate1 = 0; diff --git a/app/Models/Account.php b/app/Models/Account.php index 3388c4ad4c3b..c53205edc817 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -150,6 +150,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $companies * @property-read \Illuminate\Database\Eloquent\Collection $company_users * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class Account extends BaseModel diff --git a/app/Models/BankAccount.php b/app/Models/BankAccount.php index 48b0c78b462f..89fd1c51e4fa 100644 --- a/app/Models/BankAccount.php +++ b/app/Models/BankAccount.php @@ -37,6 +37,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts + * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @mixin \Eloquent */ class BankAccount extends BaseModel diff --git a/app/Models/BankIntegration.php b/app/Models/BankIntegration.php index d9fb837ee06f..ab6087e2b27d 100644 --- a/app/Models/BankIntegration.php +++ b/app/Models/BankIntegration.php @@ -80,6 +80,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions + * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @mixin \Eloquent */ class BankIntegration extends BaseModel diff --git a/app/Models/Client.php b/app/Models/Client.php index 8da22191e69f..7d3b8b0c4bee 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -257,6 +257,23 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices * @property-read \Illuminate\Database\Eloquent\Collection $system_logs * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Client extends BaseModel implements HasLocalePreference diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 19f3b6728c73..976a5e0e5062 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -148,6 +148,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations * @mixin \Eloquent */ class ClientContact extends Authenticatable implements HasLocalePreference diff --git a/app/Models/Company.php b/app/Models/Company.php index 9719f26a95f9..3e97194ae6e8 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -508,6 +508,50 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read \Illuminate\Database\Eloquent\Collection $vendors * @property-read \Illuminate\Database\Eloquent\Collection $webhooks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_documents + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transaction_rules + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transactions + * @property-read \Illuminate\Database\Eloquent\Collection $client_contacts + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $company_gateways + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $designs + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expense_categories + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $group_settings + * @property-read \Illuminate\Database\Eloquent\Collection $groups + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $products + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_orders + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $system_log_relation + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $task_schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $task_statuses + * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $tax_rates + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $tokens_hashed + * @property-read \Illuminate\Database\Eloquent\Collection $user_designs + * @property-read \Illuminate\Database\Eloquent\Collection $user_payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $vendors + * @property-read \Illuminate\Database\Eloquent\Collection $webhooks * @mixin \Eloquent */ class Company extends BaseModel diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 1b69207ac9e2..3d0f48dcffb0 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -97,6 +97,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @mixin \Eloquent */ class CompanyGateway extends BaseModel diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index 510093daec2b..d2470281a7df 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -82,6 +82,9 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $token * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $token + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class CompanyUser extends Pivot diff --git a/app/Models/Credit.php b/app/Models/Credit.php index e42dd8b9d80a..c0d8a93202be 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -220,6 +220,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class Credit extends BaseModel diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 27e1bcb12a74..4408b452473f 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -135,6 +135,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Expense extends BaseModel diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 8e4dc24489df..2d0095d81a1b 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -33,6 +33,7 @@ namespace App\Models; * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods + * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel diff --git a/app/Models/GroupSetting.php b/app/Models/GroupSetting.php index 637d8978e35d..8a3a1c016981 100644 --- a/app/Models/GroupSetting.php +++ b/app/Models/GroupSetting.php @@ -64,6 +64,8 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $clients * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class GroupSetting extends StaticModel diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 8b12ee6003e7..ea53537d276b 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -250,6 +250,15 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Invoice extends BaseModel diff --git a/app/Models/Payment.php b/app/Models/Payment.php index de69ef695fe1..f5bd60ca39be 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -157,6 +157,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $paymentables + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $paymentables * @mixin \Eloquent */ class Payment extends BaseModel diff --git a/app/Models/Product.php b/app/Models/Product.php index 117178af997b..f196f80c436d 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -103,6 +103,7 @@ use League\CommonMark\CommonMarkConverter; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Product extends BaseModel diff --git a/app/Models/Project.php b/app/Models/Project.php index c8690fd0aa6c..87374eff9925 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -82,6 +82,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Project extends BaseModel diff --git a/app/Models/Proposal.php b/app/Models/Proposal.php index 5c79ce24d415..2b8e68a085ab 100644 --- a/app/Models/Proposal.php +++ b/app/Models/Proposal.php @@ -33,6 +33,7 @@ use App\Utils\Traits\MakesHash; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Proposal extends BaseModel diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index 32ec9d325945..64a8f37c440f 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -210,6 +210,12 @@ use Illuminate\Support\Facades\Storage; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class PurchaseOrder extends BaseModel diff --git a/app/Models/Quote.php b/app/Models/Quote.php index d263490a0836..fedf67c0a0b1 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -201,6 +201,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @mixin \Eloquent */ class Quote extends BaseModel diff --git a/app/Models/RecurringExpense.php b/app/Models/RecurringExpense.php index 02fad06b4f11..cc77cda53cb8 100644 --- a/app/Models/RecurringExpense.php +++ b/app/Models/RecurringExpense.php @@ -144,6 +144,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class RecurringExpense extends BaseModel diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 3c0d1a533283..57d72525b39f 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -204,6 +204,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @mixin \Eloquent */ class RecurringInvoice extends BaseModel diff --git a/app/Models/RecurringQuote.php b/app/Models/RecurringQuote.php index 025ff3b22b3c..4b7085afc083 100644 --- a/app/Models/RecurringQuote.php +++ b/app/Models/RecurringQuote.php @@ -196,6 +196,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $quotes * @mixin \Eloquent */ class RecurringQuote extends BaseModel diff --git a/app/Models/Task.php b/app/Models/Task.php index 6f17f5c2d272..d0b8669ea792 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -96,6 +96,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Task extends BaseModel diff --git a/app/Models/User.php b/app/Models/User.php index 96661a9779e0..1cd305b22ef5 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -174,6 +174,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @mixin \Eloquent */ class User extends Authenticatable implements MustVerifyEmail diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index b169d3efb72d..363995acf8e3 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -124,6 +124,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $contacts * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact * @mixin \Eloquent */ class Vendor extends BaseModel diff --git a/app/Models/VendorContact.php b/app/Models/VendorContact.php index cc2110929c9b..0e70270b19fd 100644 --- a/app/Models/VendorContact.php +++ b/app/Models/VendorContact.php @@ -120,6 +120,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @mixin \Eloquent */ class VendorContact extends Authenticatable implements HasLocalePreference From c46cf68d59bfe3f8236cda1f237a25c01600ccc4 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 28 Mar 2023 17:37:38 +1100 Subject: [PATCH 14/38] Minor cleanup --- app/DataMapper/Tax/de/Rule.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index 1717a46eed77..3d2cf4823046 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -39,12 +39,15 @@ class Rule extends BaseRule implements RuleInterface public bool $foreign_consumer_tax_exempt = true; public string $tax_name1 = ''; + public float $tax_rate1 = 0; public string $tax_name2 = ''; + public float $tax_rate2 = 0; public string $tax_name3 = ''; + public float $tax_rate3 = 0; protected ?Client $client; From bd744b4ae05c959197d8a59a543aa49d6874a354 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 07:53:46 +1100 Subject: [PATCH 15/38] Taxes --- app/DataMapper/Tax/BaseRule.php | 33 +------------------ app/DataMapper/Tax/de/Rule.php | 2 +- app/DataMapper/Tax/us/Rule.php | 2 +- .../Requests/Invoice/UploadInvoiceRequest.php | 20 +++++++++++ app/Models/Product.php | 2 +- app/Utils/HtmlEngine.php | 1 - 6 files changed, 24 insertions(+), 36 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 979ab3f07b74..8835814788f8 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -20,37 +20,6 @@ class BaseRule implements RuleInterface { /** EU TAXES */ - - public array $distance_selling_thresholds = [ - "AT" => 35000, - "BE" => 35000, - "BG" => 35800, - "HR" => 35900, - "CY" => 0, // Cyprus does not have a distance selling threshold, so for cyprus buyers and sellers always use this rate - "CZ" => 44200, - "DK" => 37500, - "EE" => 35000, - "FI" => 35000, - "FR" => 35000, - "DE" => 100000, - "GR" => 35000, - "HU" => 25000, - "IE" => 35000, - "IT" => 35000, - "LV" => 35000, - "LT" => 35000, - "LU" => 100000, - "MT" => 35000, - "NL" => 100000, - "PL" => 36900, - "PT" => 35000, - "RO" => 24200, - "SK" => 35000, - "SI" => 35000, - "ES" => 35000, - "SE" => 31700 - ]; - public float $vat_rate = 19; public float $vat_threshold = 10000; public float $vat_reduced_rate = 7; @@ -202,7 +171,7 @@ class BaseRule implements RuleInterface } match ($product_tax_type) { - Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), + Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index 3d2cf4823046..b5ef00ddf915 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -96,7 +96,7 @@ class Rule extends BaseRule implements RuleInterface return $this; match($product_tax_type){ - Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), + Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index bc90457d5495..b799eb1e45dd 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -73,7 +73,7 @@ class Rule implements RuleInterface } match($product_tax_type){ - Product::PRODUCT_TAX_EXEMPT => $this->taxExempt(), + Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), diff --git a/app/Http/Requests/Invoice/UploadInvoiceRequest.php b/app/Http/Requests/Invoice/UploadInvoiceRequest.php index 93396d9ee7aa..070fb4443d1b 100644 --- a/app/Http/Requests/Invoice/UploadInvoiceRequest.php +++ b/app/Http/Requests/Invoice/UploadInvoiceRequest.php @@ -12,6 +12,7 @@ namespace App\Http\Requests\Invoice; use App\Http\Requests\Request; +use Illuminate\Http\UploadedFile; class UploadInvoiceRequest extends Request { @@ -46,5 +47,24 @@ class UploadInvoiceRequest extends Request public function prepareForValidation() { + + //tests to see if upload via binary data works. + + // if(request()->getContent()) + // { + // // $file = new UploadedFile(request()->getContent(), request()->header('filename')); + // $file = new UploadedFile(request()->getContent(), 'something.png'); + // // request()->files->set('documents', $file); + + // $this->files->add(['file' => $file]); + + // // Merge it in request also (As I found this is not needed in every case) + // $this->merge(['file' => $file]); + + + // } + + + } } diff --git a/app/Models/Product.php b/app/Models/Product.php index f196f80c436d..12a5c9c9f14c 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -117,7 +117,7 @@ class Product extends BaseModel public const PRODUCT_TYPE_SERVICE = 2; public const PRODUCT_TYPE_DIGITAL = 3; public const PRODUCT_TYPE_SHIPPING = 4; - public const PRODUCT_TAX_EXEMPT = 5; + public const PRODUCT_TYPE_EXEMPT = 5; public const PRODUCT_TYPE_REDUCED_TAX = 6; protected $fillable = [ diff --git a/app/Utils/HtmlEngine.php b/app/Utils/HtmlEngine.php index 36cd4e272206..cb51089e3207 100644 --- a/app/Utils/HtmlEngine.php +++ b/app/Utils/HtmlEngine.php @@ -374,7 +374,6 @@ class HtmlEngine $data['$user_iban'] = ['value' => $this->helpers->formatCustomFieldValue($this->company->custom_fields, 'company1', $this->settings->custom_value1, $this->client) ?: ' ', 'label' => $this->helpers->makeCustomField($this->company->custom_fields, 'company1')]; - $data['$invoice.public_notes'] = ['value' => Helpers::processReservedKeywords(\nl2br($this->entity->public_notes ?: ''), $this->client) ?: '', 'label' => ctrans('texts.public_notes')]; $data['$entity.public_notes'] = &$data['$invoice.public_notes']; $data['$public_notes'] = &$data['$invoice.public_notes']; From 93d68a70039c4e4b6ae4273622a3f248fe68628a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 08:27:13 +1100 Subject: [PATCH 16/38] Tax Model --- app/DataMapper/Tax/BaseRule.php | 7 + app/DataMapper/Tax/tax_model.yaml | 218 ++++++++++++++++++++++++++++++ app/Models/Product.php | 1 + 3 files changed, 226 insertions(+) create mode 100644 app/DataMapper/Tax/tax_model.yaml diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 8835814788f8..eae0dd2a55ba 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -177,6 +177,7 @@ class BaseRule implements RuleInterface Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), + Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), default => $this->default(), }; @@ -234,4 +235,10 @@ class BaseRule implements RuleInterface return $this; } + + public function override(): self + { + return $this; + } + } diff --git a/app/DataMapper/Tax/tax_model.yaml b/app/DataMapper/Tax/tax_model.yaml new file mode 100644 index 000000000000..1e5ad39c717b --- /dev/null +++ b/app/DataMapper/Tax/tax_model.yaml @@ -0,0 +1,218 @@ +region: + US: + tax_all: false + seller_region: CA + subregions: + AL: + APPLY_TAX: false + AK: + APPLY_TAX: false + AZ: + APPLY_TAX: false + AR: + APPLY_TAX: false + CA: + APPLY_TAX: false + CO: + APPLY_TAX: false + CT: + APPLY_TAX: false + DE: + APPLY_TAX: false + FL: + APPLY_TAX: false + GA: + APPLY_TAX: false + HI: + APPLY_TAX: false + ID: + APPLY_TAX: false + IL: + APPLY_TAX: false + IN: + APPLY_TAX: false + IA: + APPLY_TAX: false + KS: + APPLY_TAX: false + KY: + APPLY_TAX: false + LA: + APPLY_TAX: false + ME: + APPLY_TAX: false + MD: + APPLY_TAX: false + MA: + APPLY_TAX: false + MI: + APPLY_TAX: false + MN: + APPLY_TAX: false + MS: + APPLY_TAX: false + MO: + APPLY_TAX: false + MT: + APPLY_TAX: false + NE: + APPLY_TAX: false + NV: + APPLY_TAX: false + NH: + APPLY_TAX: false + NJ: + APPLY_TAX: false + NM: + APPLY_TAX: false + NY: + APPLY_TAX: false + NC: + APPLY_TAX: false + ND: + APPLY_TAX: false + OH: + APPLY_TAX: false + OK: + APPLY_TAX: false + OR: + APPLY_TAX: false + PA: + APPLY_TAX: false + RI: + APPLY_TAX: false + SC: + APPLY_TAX: false + SD: + APPLY_TAX: false + TN: + APPLY_TAX: false + TX: + APPLY_TAX: false + UT: + APPLY_TAX: false + VT: + APPLY_TAX: false + VA: + APPLY_TAX: false + WA: + APPLY_TAX: false + WV: + APPLY_TAX: false + WI: + APPLY_TAX: false + WY: + APPLY_TAX: false + EU: + tax_all: true + vat_threshold: 10000 + seller_region: DE + subregions: + AT: + VAT: 21 + REDUCED_VAT: 11 + APPLY_VAT: true + BE: + VAT: 21 + REDUCED_VAT: 6 + APPLY_VAT: true + BG: + VAT: 20 + REDUCED_VAT: 9 + APPLY_VAT: true + CY: + VAT: 19 + REDUCED_VAT: 9 + APPLY_VAT: true + CZ: + VAT: 21 + REDUCED_VAT: 15 + APPLY_VAT: true + DE: + VAT: 19 + REDUCED_VAT: 7 + APPLY_VAT: true + DK: + VAT: 25 + REDUCED_VAT: 0 + APPLY_VAT: true + EE: + VAT: 20 + REDUCED_VAT: 9 + APPLY_VAT: true + ES: + VAT: 21 + REDUCED_VAT: 10 + APPLY_VAT: true + FI: + VAT: 24 + REDUCED_VAT: 14 + APPLY_VAT: true + FR: + VAT: 20 + REDUCED_VAT: 5.5 + APPLY_VAT: true + GB: + VAT: 20 + REDUCED_VAT: 0 + APPLY_VAT: true + GR: + VAT: 24 + REDUCED_VAT: 13 + APPLY_VAT: true + HR: + VAT: 25 + REDUCED_VAT: 5 + APPLY_VAT: true + HU: + VAT: 27 + REDUCED_VAT: 5 + APPLY_VAT: true + IE: + VAT: 23 + REDUCED_VAT: 0 + APPLY_VAT: true + IT: + VAT: 22 + REDUCED_VAT: 10 + APPLY_VAT: true + LT: + VAT: 21 + REDUCED_VAT: 9 + APPLY_VAT: true + LU: + VAT: 17 + REDUCED_VAT: 3 + APPLY_VAT: true + LV: + VAT: 21 + REDUCED_VAT: 12 + APPLY_VAT: true + MT: + VAT: 18 + REDUCED_VAT: 5 + APPLY_VAT: true + NL: + VAT: 21 + REDUCED_VAT: 9 + APPLY_VAT: true + PT: + VAT: 23 + REDUCED_VAT: 6 + APPLY_VAT: true + RO: + VAT: 19 + REDUCED_VAT: 5 + APPLY_VAT: true + SE: + VAT: 25 + REDUCED_VAT: 12 + APPLY_VAT: true + SI: + VAT: 22 + REDUCED_VAT: 9.5 + APPLY_VAT: true + SK: + VAT: 20 + REDUCED_VAT: 10 + APPLY_VAT: true diff --git a/app/Models/Product.php b/app/Models/Product.php index 12a5c9c9f14c..a541bc6ff45a 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -119,6 +119,7 @@ class Product extends BaseModel public const PRODUCT_TYPE_SHIPPING = 4; public const PRODUCT_TYPE_EXEMPT = 5; public const PRODUCT_TYPE_REDUCED_TAX = 6; + public const PRODUCT_TYPE_OVERRIDE_TAX = 7; protected $fillable = [ 'custom_value1', From 78538c60df8a683f0f493dd2bdcbeb4915446a83 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 10:10:36 +1100 Subject: [PATCH 17/38] Tax Model --- app/DataMapper/Tax/tax_model.yaml | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/app/DataMapper/Tax/tax_model.yaml b/app/DataMapper/Tax/tax_model.yaml index 1e5ad39c717b..0d0eb2cdcbe8 100644 --- a/app/DataMapper/Tax/tax_model.yaml +++ b/app/DataMapper/Tax/tax_model.yaml @@ -104,115 +104,115 @@ region: WY: APPLY_TAX: false EU: - tax_all: true + tax_all: false vat_threshold: 10000 seller_region: DE subregions: AT: VAT: 21 REDUCED_VAT: 11 - APPLY_VAT: true + APPLY_VAT: false BE: VAT: 21 REDUCED_VAT: 6 - APPLY_VAT: true + APPLY_VAT: false BG: VAT: 20 REDUCED_VAT: 9 - APPLY_VAT: true + APPLY_VAT: false CY: VAT: 19 REDUCED_VAT: 9 - APPLY_VAT: true + APPLY_VAT: false CZ: VAT: 21 REDUCED_VAT: 15 - APPLY_VAT: true + APPLY_VAT: false DE: VAT: 19 REDUCED_VAT: 7 - APPLY_VAT: true + APPLY_VAT: false DK: VAT: 25 REDUCED_VAT: 0 - APPLY_VAT: true + APPLY_VAT: false EE: VAT: 20 REDUCED_VAT: 9 - APPLY_VAT: true + APPLY_VAT: false ES: VAT: 21 REDUCED_VAT: 10 - APPLY_VAT: true + APPLY_VAT: false FI: VAT: 24 REDUCED_VAT: 14 - APPLY_VAT: true + APPLY_VAT: false FR: VAT: 20 REDUCED_VAT: 5.5 - APPLY_VAT: true + APPLY_VAT: false GB: VAT: 20 REDUCED_VAT: 0 - APPLY_VAT: true + APPLY_VAT: false GR: VAT: 24 REDUCED_VAT: 13 - APPLY_VAT: true + APPLY_VAT: false HR: VAT: 25 REDUCED_VAT: 5 - APPLY_VAT: true + APPLY_VAT: false HU: VAT: 27 REDUCED_VAT: 5 - APPLY_VAT: true + APPLY_VAT: false IE: VAT: 23 REDUCED_VAT: 0 - APPLY_VAT: true + APPLY_VAT: false IT: VAT: 22 REDUCED_VAT: 10 - APPLY_VAT: true + APPLY_VAT: false LT: VAT: 21 REDUCED_VAT: 9 - APPLY_VAT: true + APPLY_VAT: false LU: VAT: 17 REDUCED_VAT: 3 - APPLY_VAT: true + APPLY_VAT: false LV: VAT: 21 REDUCED_VAT: 12 - APPLY_VAT: true + APPLY_VAT: false MT: VAT: 18 REDUCED_VAT: 5 - APPLY_VAT: true + APPLY_VAT: false NL: VAT: 21 REDUCED_VAT: 9 - APPLY_VAT: true + APPLY_VAT: false PT: VAT: 23 REDUCED_VAT: 6 - APPLY_VAT: true + APPLY_VAT: false RO: VAT: 19 REDUCED_VAT: 5 - APPLY_VAT: true + APPLY_VAT: false SE: VAT: 25 REDUCED_VAT: 12 - APPLY_VAT: true + APPLY_VAT: false SI: VAT: 22 REDUCED_VAT: 9.5 - APPLY_VAT: true + APPLY_VAT: false SK: VAT: 20 REDUCED_VAT: 10 - APPLY_VAT: true + APPLY_VAT: false From 1ab8097d44de9c61f2200364988bef05c02fb7a0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 13:13:50 +1100 Subject: [PATCH 18/38] Refactor for tax structure --- app/DataMapper/Tax/BaseRule.php | 141 +------- app/DataMapper/Tax/RuleInterface.php | 2 + app/DataMapper/Tax/TaxModel.php | 316 ++++++++++++++++++ app/DataMapper/Tax/ZipTax/Response.php | 9 + app/DataMapper/Tax/de/Rule.php | 17 +- app/DataMapper/Tax/tax_model.yaml | 264 +++++++-------- app/DataMapper/Tax/us/Rule.php | 7 + app/Models/Account.php | 4 + app/Models/BankAccount.php | 1 + app/Models/BankIntegration.php | 1 + app/Models/Client.php | 17 + app/Models/ClientContact.php | 5 + app/Models/Company.php | 44 +++ app/Models/CompanyGateway.php | 1 + app/Models/CompanyUser.php | 3 + app/Models/Credit.php | 7 + app/Models/Expense.php | 1 + app/Models/GatewayType.php | 1 + app/Models/GroupSetting.php | 2 + app/Models/Invoice.php | 9 + app/Models/Payment.php | 5 + app/Models/Product.php | 1 + app/Models/Project.php | 2 + app/Models/Proposal.php | 1 + app/Models/PurchaseOrder.php | 6 + app/Models/Quote.php | 4 + app/Models/RecurringExpense.php | 1 + app/Models/RecurringInvoice.php | 5 + app/Models/RecurringQuote.php | 5 + app/Models/Task.php | 1 + app/Models/User.php | 7 + app/Models/Vendor.php | 4 + app/Models/VendorContact.php | 2 + app/Services/Tax/Providers/EuTax.php | 46 ++- app/Transformers/ClientTransformer.php | 1 + app/Transformers/CompanyTransformer.php | 1 + ...054758_add_client_is_exempt_from_taxes.php | 9 +- tests/Unit/Tax/EuTaxTest.php | 24 +- 38 files changed, 678 insertions(+), 299 deletions(-) create mode 100644 app/DataMapper/Tax/TaxModel.php diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index eae0dd2a55ba..fd7cfea6da3b 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -11,110 +11,22 @@ namespace App\DataMapper\Tax; -use App\Models\Client; -use App\Models\Product; -use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\ZipTax\Response; +use App\Models\Client; class BaseRule implements RuleInterface { - /** EU TAXES */ - public float $vat_rate = 19; - public float $vat_threshold = 10000; - public float $vat_reduced_rate = 7; - public float $vat_reduced_threshold = 10000; - public float $at_vat_rate = 20; // Austria - public float $be_vat_rate = 21; // Belgium - public float $bg_vat_rate = 20; // Bulgaria - public float $hr_vat_rate = 25; // Croatia - public float $cy_vat_rate = 19; // Cyprus - public float $cz_vat_rate = 21; // Czech Republic - public float $dk_vat_rate = 25; // Denmark - public float $ee_vat_rate = 20; // Estonia - public float $fi_vat_rate = 24; // Finland - public float $fr_vat_rate = 20; // France - public float $de_vat_rate = 19; // Germany - public float $gr_vat_rate = 24; // Greece - public float $hu_vat_rate = 27; // Hungary - public float $ie_vat_rate = 23; // Ireland - public float $it_vat_rate = 22; // Italy - public float $lv_vat_rate = 21; // Latvia - public float $lt_vat_rate = 21; // Lithuania - public float $lu_vat_rate = 17; // Luxembourg - public float $mt_vat_rate = 18; // Malta - public float $nl_vat_rate = 21; // Netherlands - public float $pl_vat_rate = 23; // Poland - public float $pt_vat_rate = 23; // Portugal - public float $ro_vat_rate = 19; // Romania - public float $sk_vat_rate = 20; // Slovakia - public float $si_vat_rate = 22; // Slovenia - public float $es_vat_rate = 21; // Spain - public float $se_vat_rate = 25; // Sweden - public float $gb_vat_rate = 20; // United Kingdom public bool $consumer_tax_exempt = false; public bool $business_tax_exempt = true; public bool $eu_business_tax_exempt = true; public bool $foreign_business_tax_exempt = true; public bool $foreign_consumer_tax_exempt = true; -/** EU TAXES */ + /** EU TAXES */ -/** US TAXES */ - public float $al_sales_tax_rate = 4; // Alabama - public float $ak_sales_tax_rate = 0; // Alaska - public float $az_sales_tax_rate = 5.6; // Arizona - public float $ar_sales_tax_rate = 6.5; // Arkansas - public float $ca_sales_tax_rate = 7.25; // California - https://services.maps.cdtfa.ca.gov/api/taxrate/GetRateByAddress?address=2444+s+alameda+st&city=los+angeles&zip=90058 - public float $co_sales_tax_rate = 2.9; // Colorado - public float $ct_sales_tax_rate = 6.35; // Connecticut - public float $de_sales_tax_rate = 0; // Delaware - public float $fl_sales_tax_rate = 6; // Florida - public float $ga_sales_tax_rate = 4; // Georgia - public float $hi_sales_tax_rate = 4; // Hawaii - public float $id_sales_tax_rate = 6; // Idaho - public float $il_sales_tax_rate = 6.25; // Illinois - public float $in_sales_tax_rate = 7; // Indiana - public float $ia_sales_tax_rate = 6; // Iowa - public float $ks_sales_tax_rate = 6.5; // Kansas - public float $ky_sales_tax_rate = 6; // Kentucky - public float $la_sales_tax_rate = 4.45; // Louisiana - public float $me_sales_tax_rate = 5.5; // Maine - public float $md_sales_tax_rate = 6; // Maryland - public float $ma_sales_tax_rate = 6.25; // Massachusetts - public float $mi_sales_tax_rate = 6; // Michigan - public float $mn_sales_tax_rate = 6.875; // Minnesota - public float $ms_sales_tax_rate = 7; // Mississippi - public float $mo_sales_tax_rate = 4.225; // Missouri - public float $mt_sales_tax_rate = 0; // Montana - public float $ne_sales_tax_rate = 5.5; // Nebraska - public float $nv_sales_tax_rate = 6.85; // Nevada - public float $nh_sales_tax_rate = 0; // New Hampshire - public float $nj_sales_tax_rate = 6.625; // New Jersey - public float $nm_sales_tax_rate = 5.125; // New Mexico - public float $ny_sales_tax_rate = 4; // New York - public float $nc_sales_tax_rate = 4.75; // North Carolina - public float $nd_sales_tax_rate = 5; // North Dakota - public float $oh_sales_tax_rate = 5.75; // Ohio - public float $ok_sales_tax_rate = 4.5; // Oklahoma - public float $or_sales_tax_rate = 0; // Oregon - public float $pa_sales_tax_rate = 6; // Pennsylvania - public float $ri_sales_tax_rate = 7; // Rhode Island - public float $sc_sales_tax_rate = 6; // South Carolina - public float $sd_sales_tax_rate = 4.5; // South Dakota - public float $tn_sales_tax_rate = 7; // Tennessee - public float $tx_sales_tax_rate = 6.25; // Texas - public float $ut_sales_tax_rate = 4.7; // Utah - public float $vt_sales_tax_rate = 6; // Vermont - public float $va_sales_tax_rate = 5.3; // Virginia - public float $wa_sales_tax_rate = 6.5; // Washington - public float $wv_sales_tax_rate = 6; // West Virginia - public float $wi_sales_tax_rate = 5; // Wisconsin - public float $wy_sales_tax_rate = 4; // Wyoming - public float $dc_sales_tax_rate = 6; // District of Columbia - public float $pr_sales_tax_rate = 11.5; // Puerto Rico -/**US TAXES */ - + /** US TAXES */ + /** US TAXES */ public string $tax_name1 = ''; public float $tax_rate1 = 0; @@ -147,92 +59,48 @@ class BaseRule implements RuleInterface return $this; } - //need to add logic here to capture if public function tax(): self { - if ($this->client->is_tax_exempt || $this->client->has_valid_vat_number) { - return $this->taxExempt(); - } - - $this->tax_name1 = $this->vat_rate; - $this->tax_rate1 = "VAT"; - return $this; } public function taxByType(?int $product_tax_type): self { - if ($this->client->is_tax_exempt) { - return $this->taxExempt(); - } - - if (!$product_tax_type) { - return $this; - } - - match ($product_tax_type) { - Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), - Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), - Product::PRODUCT_TYPE_SERVICE => $this->taxService(), - Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), - Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), - Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), - Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), - default => $this->default(), - }; - return $this; } public function taxReduced(): self { - $this->tax_rate1 = $this->vat_reduced_rate; - $this->tax_name1 = 'Reduced VAT'; - return $this; } public function taxExempt(): self { - $this->tax_name1 = ''; - $this->tax_rate1 = 0; - return $this; } public function taxDigital(): self { - $this->tax(); - return $this; } public function taxService(): self { - $this->tax(); - return $this; } public function taxShipping(): self { - $this->tax(); - return $this; } public function taxPhysical(): self { - $this->tax(); - return $this; } public function default(): self { - $this->tax_name1 = ''; - $this->tax_rate1 = 0; - return $this; } @@ -240,5 +108,4 @@ class BaseRule implements RuleInterface { return $this; } - } diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index 633ba0b7be8e..8ee20a3866a1 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -34,6 +34,8 @@ interface RuleInterface public function default(); + public function override(); + public function setClient(Client $client); public function setTaxData(Response $tax_data); diff --git a/app/DataMapper/Tax/TaxModel.php b/app/DataMapper/Tax/TaxModel.php new file mode 100644 index 000000000000..99b85764451c --- /dev/null +++ b/app/DataMapper/Tax/TaxModel.php @@ -0,0 +1,316 @@ +model) + $this->regions = $this->init(); + else + $this->regions = $model; + + } + + public function init() + { + $this->regions = new \stdClass(); + $this->regions->US = new \stdClass(); + $this->regions->EU = new \stdClass(); + + $this->usRegion() + ->euRegion(); + + + return $this->regions; + } + + private function usRegion(): self + { + $this->regions->US->has_sales_above_threshold = false; + $this->regions->US->tax_all = false; + $this->usSubRegions(); + + return $this; + } + + private function euRegion(): self + { + + $this->regions->EU->has_sales_above_threshold = false; + $this->regions->EU->tax_all = false; + $this->regions->EU->vat_threshold = 10000; + $this->euSubRegions(); + + return $this; + } + + private function usSubRegions(): self + { + $this->regions->US->subregions = new \stdClass(); + $this->regions->US->subregions->AL = new \stdClass(); + $this->regions->US->subregions->AL->apply_tax = false; + $this->regions->US->subregions->AK = new \stdClass(); + $this->regions->US->subregions->AK->apply_tax = false; + $this->regions->US->subregions->AZ = new \stdClass(); + $this->regions->US->subregions->AZ->apply_tax = false; + $this->regions->US->subregions->AR = new \stdClass(); + $this->regions->US->subregions->AR->apply_tax = false; + $this->regions->US->subregions->CA = new \stdClass(); + $this->regions->US->subregions->CA->apply_tax = false; + $this->regions->US->subregions->CO = new \stdClass(); + $this->regions->US->subregions->CO->apply_tax = false; + $this->regions->US->subregions->CT = new \stdClass(); + $this->regions->US->subregions->CT->apply_tax = false; + $this->regions->US->subregions->DE = new \stdClass(); + $this->regions->US->subregions->DE->apply_tax = false; + $this->regions->US->subregions->FL = new \stdClass(); + $this->regions->US->subregions->FL->apply_tax = false; + $this->regions->US->subregions->GA = new \stdClass(); + $this->regions->US->subregions->GA->apply_tax = false; + $this->regions->US->subregions->HI = new \stdClass(); + $this->regions->US->subregions->HI->apply_tax = false; + $this->regions->US->subregions->ID = new \stdClass(); + $this->regions->US->subregions->ID->apply_tax = false; + $this->regions->US->subregions->IL = new \stdClass(); + $this->regions->US->subregions->IL->apply_tax = false; + $this->regions->US->subregions->IN = new \stdClass(); + $this->regions->US->subregions->IN->apply_tax = false; + $this->regions->US->subregions->IA = new \stdClass(); + $this->regions->US->subregions->IA->apply_tax = false; + $this->regions->US->subregions->KS = new \stdClass(); + $this->regions->US->subregions->KS->apply_tax = false; + $this->regions->US->subregions->KY = new \stdClass(); + $this->regions->US->subregions->KY->apply_tax = false; + $this->regions->US->subregions->LA = new \stdClass(); + $this->regions->US->subregions->LA->apply_tax = false; + $this->regions->US->subregions->ME = new \stdClass(); + $this->regions->US->subregions->ME->apply_tax = false; + $this->regions->US->subregions->MD = new \stdClass(); + $this->regions->US->subregions->MD->apply_tax = false; + $this->regions->US->subregions->MA = new \stdClass(); + $this->regions->US->subregions->MA->apply_tax = false; + $this->regions->US->subregions->MI = new \stdClass(); + $this->regions->US->subregions->MI->apply_tax = false; + $this->regions->US->subregions->MN = new \stdClass(); + $this->regions->US->subregions->MN->apply_tax = false; + $this->regions->US->subregions->MS = new \stdClass(); + $this->regions->US->subregions->MS->apply_tax = false; + $this->regions->US->subregions->MO = new \stdClass(); + $this->regions->US->subregions->MO->apply_tax = false; + $this->regions->US->subregions->MT = new \stdClass(); + $this->regions->US->subregions->MT->apply_tax = false; + $this->regions->US->subregions->NE = new \stdClass(); + $this->regions->US->subregions->NE->apply_tax = false; + $this->regions->US->subregions->NV = new \stdClass(); + $this->regions->US->subregions->NV->apply_tax = false; + $this->regions->US->subregions->NH = new \stdClass(); + $this->regions->US->subregions->NH->apply_tax = false; + $this->regions->US->subregions->NJ = new \stdClass(); + $this->regions->US->subregions->NJ->apply_tax = false; + $this->regions->US->subregions->NM = new \stdClass(); + $this->regions->US->subregions->NM->apply_tax = false; + $this->regions->US->subregions->NY = new \stdClass(); + $this->regions->US->subregions->NY->apply_tax = false; + $this->regions->US->subregions->NC = new \stdClass(); + $this->regions->US->subregions->NC->apply_tax = false; + $this->regions->US->subregions->ND = new \stdClass(); + $this->regions->US->subregions->ND->apply_tax = false; + $this->regions->US->subregions->OH = new \stdClass(); + $this->regions->US->subregions->OH->apply_tax = false; + $this->regions->US->subregions->OK = new \stdClass(); + $this->regions->US->subregions->OK->apply_tax = false; + $this->regions->US->subregions->OR = new \stdClass(); + $this->regions->US->subregions->OR->apply_tax = false; + $this->regions->US->subregions->PA = new \stdClass(); + $this->regions->US->subregions->PA->apply_tax = false; + $this->regions->US->subregions->RI = new \stdClass(); + $this->regions->US->subregions->RI->apply_tax = false; + $this->regions->US->subregions->SC = new \stdClass(); + $this->regions->US->subregions->SC->apply_tax = false; + $this->regions->US->subregions->SD = new \stdClass(); + $this->regions->US->subregions->SD->apply_tax = false; + $this->regions->US->subregions->TN = new \stdClass(); + $this->regions->US->subregions->TN->apply_tax = false; + $this->regions->US->subregions->TX = new \stdClass(); + $this->regions->US->subregions->TX->apply_tax = false; + $this->regions->US->subregions->UT = new \stdClass(); + $this->regions->US->subregions->UT->apply_tax = false; + $this->regions->US->subregions->VT = new \stdClass(); + $this->regions->US->subregions->VT->apply_tax = false; + $this->regions->US->subregions->VA = new \stdClass(); + $this->regions->US->subregions->VA->apply_tax = false; + $this->regions->US->subregions->WA = new \stdClass(); + $this->regions->US->subregions->WA->apply_tax = false; + $this->regions->US->subregions->WV = new \stdClass(); + $this->regions->US->subregions->WV->apply_tax = false; + $this->regions->US->subregions->WI = new \stdClass(); + $this->regions->US->subregions->WI->apply_tax = false; + $this->regions->US->subregions->WY = new \stdClass(); + $this->regions->US->subregions->WY->apply_tax = false; + + return $this; + } + + private function euSubRegions(): self + { + + $this->regions->EU->subregions = new \stdClass(); + + $this->regions->EU->subregions->AT = new \stdClass(); + $this->regions->EU->subregions->AT->vat_rate = 21; + $this->regions->EU->subregions->AT->reduced_vat_rate = 11; + $this->regions->EU->subregions->AT->apply_tax = false; + + $this->regions->EU->subregions->BE = new \stdClass(); + $this->regions->EU->subregions->BE->vat_rate = 21; + $this->regions->EU->subregions->BE->reduced_vat_rate = 6; + $this->regions->EU->subregions->BE->apply_tax = false; + + $this->regions->EU->subregions->BG = new \stdClass(); + $this->regions->EU->subregions->BG->vat_rate = 20; + $this->regions->EU->subregions->BG->reduced_vat_rate = 9; + $this->regions->EU->subregions->BG->apply_tax = false; + + $this->regions->EU->subregions->CY = new \stdClass(); + $this->regions->EU->subregions->CY->vat_rate = 19; + $this->regions->EU->subregions->CY->reduced_vat_rate = 9; + $this->regions->EU->subregions->CY->apply_tax = false; + + $this->regions->EU->subregions->CZ = new \stdClass(); + $this->regions->EU->subregions->CZ->vat_rate = 21; + $this->regions->EU->subregions->CZ->reduced_vat_rate = 15; + $this->regions->EU->subregions->CZ->apply_tax = false; + + $this->regions->EU->subregions->DE = new \stdClass(); + $this->regions->EU->subregions->DE->vat_rate = 19; + $this->regions->EU->subregions->DE->reduced_vat_rate = 7; + $this->regions->EU->subregions->DE->apply_tax = false; + + $this->regions->EU->subregions->DK = new \stdClass(); + $this->regions->EU->subregions->DK->vat_rate = 25; + $this->regions->EU->subregions->DK->reduced_vat_rate = 0; + $this->regions->EU->subregions->DK->apply_tax = false; + + $this->regions->EU->subregions->EE = new \stdClass(); + $this->regions->EU->subregions->EE->vat_rate = 20; + $this->regions->EU->subregions->EE->reduced_vat_rate = 9; + $this->regions->EU->subregions->EE->apply_tax = false; + + $this->regions->EU->subregions->ES = new \stdClass(); + $this->regions->EU->subregions->ES->vat_rate = 21; + $this->regions->EU->subregions->ES->reduced_vat_rate = 10; + $this->regions->EU->subregions->ES->apply_tax = false; + + $this->regions->EU->subregions->FI = new \stdClass(); + $this->regions->EU->subregions->FI->vat_rate = 24; + $this->regions->EU->subregions->FI->reduced_vat_rate = 14; + $this->regions->EU->subregions->FI->apply_tax = false; + + $this->regions->EU->subregions->FR = new \stdClass(); + $this->regions->EU->subregions->FR->vat_rate = 20; + $this->regions->EU->subregions->FR->reduced_vat_rate = 5.5; + $this->regions->EU->subregions->FR->apply_tax = false; + + $this->regions->EU->subregions->GB = new \stdClass(); + $this->regions->EU->subregions->GB->vat_rate = 20; + $this->regions->EU->subregions->GB->reduced_vat_rate = 0; + $this->regions->EU->subregions->GB->apply_tax = false; + + $this->regions->EU->subregions->GR = new \stdClass(); + $this->regions->EU->subregions->GR->vat_rate = 24; + $this->regions->EU->subregions->GR->reduced_vat_rate = 13; + $this->regions->EU->subregions->GR->apply_tax = false; + + $this->regions->EU->subregions->HR = new \stdClass(); + $this->regions->EU->subregions->HR->vat_rate = 25; + $this->regions->EU->subregions->HR->reduced_vat_rate = 5; + $this->regions->EU->subregions->HR->apply_tax = false; + + $this->regions->EU->subregions->HU = new \stdClass(); + $this->regions->EU->subregions->HU->vat_rate = 27; + $this->regions->EU->subregions->HU->reduced_vat_rate = 5; + $this->regions->EU->subregions->HU->apply_tax = false; + + $this->regions->EU->subregions->IE = new \stdClass(); + $this->regions->EU->subregions->IE->vat_rate = 23; + $this->regions->EU->subregions->IE->reduced_vat_rate = 0; + $this->regions->EU->subregions->IE->apply_tax = false; + + $this->regions->EU->subregions->IT = new \stdClass(); + $this->regions->EU->subregions->IT->vat_rate = 22; + $this->regions->EU->subregions->IT->reduced_vat_rate = 10; + $this->regions->EU->subregions->IT->apply_tax = false; + + $this->regions->EU->subregions->LT = new \stdClass(); + $this->regions->EU->subregions->LT->vat_rate = 21; + $this->regions->EU->subregions->LT->reduced_vat_rate = 9; + $this->regions->EU->subregions->LT->apply_tax = false; + + $this->regions->EU->subregions->LU = new \stdClass(); + $this->regions->EU->subregions->LU->vat_rate = 17; + $this->regions->EU->subregions->LU->reduced_vat_rate = 3; + $this->regions->EU->subregions->LU->apply_tax = false; + + $this->regions->EU->subregions->LV = new \stdClass(); + $this->regions->EU->subregions->LV->vat_rate = 21; + $this->regions->EU->subregions->LV->reduced_vat_rate = 12; + $this->regions->EU->subregions->LV->apply_tax = false; + + $this->regions->EU->subregions->MT = new \stdClass(); + $this->regions->EU->subregions->MT->vat_rate = 18; + $this->regions->EU->subregions->MT->reduced_vat_rate = 5; + $this->regions->EU->subregions->MT->apply_tax = false; + + $this->regions->EU->subregions->NL = new \stdClass(); + $this->regions->EU->subregions->NL->vat_rate = 21; + $this->regions->EU->subregions->NL->reduced_vat_rate = 9; + $this->regions->EU->subregions->NL->apply_tax = false; + + $this->regions->EU->subregions->PT = new \stdClass(); + $this->regions->EU->subregions->PT->vat_rate = 23; + $this->regions->EU->subregions->PT->reduced_vat_rate = 6; + $this->regions->EU->subregions->PT->apply_tax = false; + + $this->regions->EU->subregions->RO = new \stdClass(); + $this->regions->EU->subregions->RO->vat_rate = 19; + $this->regions->EU->subregions->RO->reduced_vat_rate = 5; + $this->regions->EU->subregions->RO->apply_tax = false; + + $this->regions->EU->subregions->SE = new \stdClass(); + $this->regions->EU->subregions->SE->vat_rate = 25; + $this->regions->EU->subregions->SE->reduced_vat_rate = 12; + $this->regions->EU->subregions->SE->apply_tax = false; + + $this->regions->EU->subregions->SI = new \stdClass(); + $this->regions->EU->subregions->SI->vat_rate = 22; + $this->regions->EU->subregions->SI->reduced_vat_rate = 9.5; + $this->regions->EU->subregions->SI->apply_tax = false; + + $this->regions->EU->subregions->SK = new \stdClass(); + $this->regions->EU->subregions->SK->vat_rate = 20; + $this->regions->EU->subregions->SK->reduced_vat_rate = 10; + $this->regions->EU->subregions->SK->apply_tax = false; + + return $this; + + } + +} diff --git a/app/DataMapper/Tax/ZipTax/Response.php b/app/DataMapper/Tax/ZipTax/Response.php index 73b510b74954..8ea3d7d4b307 100644 --- a/app/DataMapper/Tax/ZipTax/Response.php +++ b/app/DataMapper/Tax/ZipTax/Response.php @@ -57,6 +57,8 @@ class Response * ]; * */ + public string $seller_region = ""; + //US public string $geoPostalCode = ""; public string $geoCity = ""; @@ -93,6 +95,13 @@ class Response public float $district5UseTax = 0; public string $originDestination = ""; + //EU + public float $vat_rate = 0; + public float $vat_reduced_rate = 0; + public string $vat_country_code = ""; + + + public function __construct($data) { diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/de/Rule.php index b5ef00ddf915..54f33978ef07 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/de/Rule.php @@ -20,17 +20,9 @@ use App\DataMapper\Tax\ZipTax\Response; class Rule extends BaseRule implements RuleInterface { - public float $vat_rate = 19; - - public float $vat_threshold = 10000; - - public float $vat_reduced_rate = 7; - - public float $vat_reduced_threshold = 10000; - public bool $consumer_tax_exempt = false; - public bool $business_tax_exempt = true; + public bool $business_tax_exempt = false; public bool $eu_business_tax_exempt = true; @@ -102,6 +94,7 @@ class Rule extends BaseRule implements RuleInterface Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), + Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), default => $this->default(), }; @@ -160,4 +153,10 @@ class Rule extends BaseRule implements RuleInterface return $this; } + + public function override(): self + { + return $this; + } + } diff --git a/app/DataMapper/Tax/tax_model.yaml b/app/DataMapper/Tax/tax_model.yaml index 0d0eb2cdcbe8..1288ad6320ff 100644 --- a/app/DataMapper/Tax/tax_model.yaml +++ b/app/DataMapper/Tax/tax_model.yaml @@ -2,217 +2,219 @@ region: US: tax_all: false seller_region: CA + has_sales_above_threshold: false subregions: AL: - APPLY_TAX: false + apply_tax: false AK: - APPLY_TAX: false + apply_tax: false AZ: - APPLY_TAX: false + apply_tax: false AR: - APPLY_TAX: false + apply_tax: false CA: - APPLY_TAX: false + apply_tax: false CO: - APPLY_TAX: false + apply_tax: false CT: - APPLY_TAX: false + apply_tax: false DE: - APPLY_TAX: false + apply_tax: false FL: - APPLY_TAX: false + apply_tax: false GA: - APPLY_TAX: false + apply_tax: false HI: - APPLY_TAX: false + apply_tax: false ID: - APPLY_TAX: false + apply_tax: false IL: - APPLY_TAX: false + apply_tax: false IN: - APPLY_TAX: false + apply_tax: false IA: - APPLY_TAX: false + apply_tax: false KS: - APPLY_TAX: false + apply_tax: false KY: - APPLY_TAX: false + apply_tax: false LA: - APPLY_TAX: false + apply_tax: false ME: - APPLY_TAX: false + apply_tax: false MD: - APPLY_TAX: false + apply_tax: false MA: - APPLY_TAX: false + apply_tax: false MI: - APPLY_TAX: false + apply_tax: false MN: - APPLY_TAX: false + apply_tax: false MS: - APPLY_TAX: false + apply_tax: false MO: - APPLY_TAX: false + apply_tax: false MT: - APPLY_TAX: false + apply_tax: false NE: - APPLY_TAX: false + apply_tax: false NV: - APPLY_TAX: false + apply_tax: false NH: - APPLY_TAX: false + apply_tax: false NJ: - APPLY_TAX: false + apply_tax: false NM: - APPLY_TAX: false + apply_tax: false NY: - APPLY_TAX: false + apply_tax: false NC: - APPLY_TAX: false + apply_tax: false ND: - APPLY_TAX: false + apply_tax: false OH: - APPLY_TAX: false + apply_tax: false OK: - APPLY_TAX: false + apply_tax: false OR: - APPLY_TAX: false + apply_tax: false PA: - APPLY_TAX: false + apply_tax: false RI: - APPLY_TAX: false + apply_tax: false SC: - APPLY_TAX: false + apply_tax: false SD: - APPLY_TAX: false + apply_tax: false TN: - APPLY_TAX: false + apply_tax: false TX: - APPLY_TAX: false + apply_tax: false UT: - APPLY_TAX: false + apply_tax: false VT: - APPLY_TAX: false + apply_tax: false VA: - APPLY_TAX: false + apply_tax: false WA: - APPLY_TAX: false + apply_tax: false WV: - APPLY_TAX: false + apply_tax: false WI: - APPLY_TAX: false + apply_tax: false WY: - APPLY_TAX: false + apply_tax: false EU: tax_all: false vat_threshold: 10000 + has_sales_above_threshold: false seller_region: DE subregions: AT: - VAT: 21 - REDUCED_VAT: 11 - APPLY_VAT: false + vat: 21 + reduced_vat: 11 + apply_tax: false BE: - VAT: 21 - REDUCED_VAT: 6 - APPLY_VAT: false + vat: 21 + reduced_vat: 6 + apply_tax: false BG: - VAT: 20 - REDUCED_VAT: 9 - APPLY_VAT: false + vat: 20 + reduced_vat: 9 + apply_tax: false CY: - VAT: 19 - REDUCED_VAT: 9 - APPLY_VAT: false + vat: 19 + reduced_vat: 9 + apply_tax: false CZ: - VAT: 21 - REDUCED_VAT: 15 - APPLY_VAT: false + vat: 21 + reduced_vat: 15 + apply_tax: false DE: - VAT: 19 - REDUCED_VAT: 7 - APPLY_VAT: false + vat: 19 + reduced_vat: 7 + apply_tax: false DK: - VAT: 25 - REDUCED_VAT: 0 - APPLY_VAT: false + vat: 25 + reduced_vat: 0 + apply_tax: false EE: - VAT: 20 - REDUCED_VAT: 9 - APPLY_VAT: false + vat: 20 + reduced_vat: 9 + apply_tax: false ES: - VAT: 21 - REDUCED_VAT: 10 - APPLY_VAT: false + vat: 21 + reduced_vat: 10 + apply_tax: false FI: - VAT: 24 - REDUCED_VAT: 14 - APPLY_VAT: false + vat: 24 + reduced_vat: 14 + apply_tax: false FR: - VAT: 20 - REDUCED_VAT: 5.5 - APPLY_VAT: false + vat: 20 + reduced_vat: 5.5 + apply_tax: false GB: - VAT: 20 - REDUCED_VAT: 0 - APPLY_VAT: false + vat: 20 + reduced_vat: 0 + apply_tax: false GR: - VAT: 24 - REDUCED_VAT: 13 - APPLY_VAT: false + vat: 24 + reduced_vat: 13 + apply_tax: false HR: - VAT: 25 - REDUCED_VAT: 5 - APPLY_VAT: false + vat: 25 + reduced_vat: 5 + apply_tax: false HU: - VAT: 27 - REDUCED_VAT: 5 - APPLY_VAT: false + vat: 27 + reduced_vat: 5 + apply_tax: false IE: - VAT: 23 - REDUCED_VAT: 0 - APPLY_VAT: false + vat: 23 + reduced_vat: 0 + apply_tax: false IT: - VAT: 22 - REDUCED_VAT: 10 - APPLY_VAT: false + vat: 22 + reduced_vat: 10 + apply_tax: false LT: - VAT: 21 - REDUCED_VAT: 9 - APPLY_VAT: false + vat: 21 + reduced_vat: 9 + apply_tax: false LU: - VAT: 17 - REDUCED_VAT: 3 - APPLY_VAT: false + vat: 17 + reduced_vat: 3 + apply_tax: false LV: - VAT: 21 - REDUCED_VAT: 12 - APPLY_VAT: false + vat: 21 + reduced_vat: 12 + apply_tax: false MT: - VAT: 18 - REDUCED_VAT: 5 - APPLY_VAT: false + vat: 18 + reduced_vat: 5 + apply_tax: false NL: - VAT: 21 - REDUCED_VAT: 9 - APPLY_VAT: false + vat: 21 + reduced_vat: 9 + apply_tax: false PT: - VAT: 23 - REDUCED_VAT: 6 - APPLY_VAT: false + vat: 23 + reduced_vat: 6 + apply_tax: false RO: - VAT: 19 - REDUCED_VAT: 5 - APPLY_VAT: false + vat: 19 + reduced_vat: 5 + apply_tax: false SE: - VAT: 25 - REDUCED_VAT: 12 - APPLY_VAT: false + vat: 25 + reduced_vat: 12 + apply_tax: false SI: - VAT: 22 - REDUCED_VAT: 9.5 - APPLY_VAT: false + vat: 22 + reduced_vat: 9.5 + apply_tax: false SK: - VAT: 20 - REDUCED_VAT: 10 - APPLY_VAT: false + vat: 20 + reduced_vat: 10 + apply_tax: false diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/us/Rule.php index b799eb1e45dd..c465b7a82a87 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/us/Rule.php @@ -36,6 +36,11 @@ class Rule implements RuleInterface { } + public function override() + { + return $this; + } + public function setTaxData(Response $tax_data): self { $this->tax_data = $tax_data; @@ -78,6 +83,8 @@ class Rule implements RuleInterface Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), + Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(), + Product::PRODUCT_TYPE_OVERRIDE_TAX => $this->override(), default => $this->default(), }; diff --git a/app/Models/Account.php b/app/Models/Account.php index c53205edc817..a603a2582c4e 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -154,6 +154,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $companies * @property-read \Illuminate\Database\Eloquent\Collection $company_users * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class Account extends BaseModel diff --git a/app/Models/BankAccount.php b/app/Models/BankAccount.php index 89fd1c51e4fa..0762baf121dc 100644 --- a/app/Models/BankAccount.php +++ b/app/Models/BankAccount.php @@ -38,6 +38,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts + * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @mixin \Eloquent */ class BankAccount extends BaseModel diff --git a/app/Models/BankIntegration.php b/app/Models/BankIntegration.php index ab6087e2b27d..e5908e34a506 100644 --- a/app/Models/BankIntegration.php +++ b/app/Models/BankIntegration.php @@ -81,6 +81,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions + * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @mixin \Eloquent */ class BankIntegration extends BaseModel diff --git a/app/Models/Client.php b/app/Models/Client.php index 7d3b8b0c4bee..36286c79229c 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -274,6 +274,23 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices * @property-read \Illuminate\Database\Eloquent\Collection $system_logs * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Client extends BaseModel implements HasLocalePreference diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 976a5e0e5062..4060c4588c81 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -153,6 +153,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations * @mixin \Eloquent */ class ClientContact extends Authenticatable implements HasLocalePreference diff --git a/app/Models/Company.php b/app/Models/Company.php index 3e97194ae6e8..8abf25eaf6cd 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -552,6 +552,50 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read \Illuminate\Database\Eloquent\Collection $vendors * @property-read \Illuminate\Database\Eloquent\Collection $webhooks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_documents + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transaction_rules + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transactions + * @property-read \Illuminate\Database\Eloquent\Collection $client_contacts + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $company_gateways + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $designs + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expense_categories + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $group_settings + * @property-read \Illuminate\Database\Eloquent\Collection $groups + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $products + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_orders + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $system_log_relation + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $task_schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $task_statuses + * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $tax_rates + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $tokens_hashed + * @property-read \Illuminate\Database\Eloquent\Collection $user_designs + * @property-read \Illuminate\Database\Eloquent\Collection $user_payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $vendors + * @property-read \Illuminate\Database\Eloquent\Collection $webhooks * @mixin \Eloquent */ class Company extends BaseModel diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 3d0f48dcffb0..4b51a9d91fc9 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -98,6 +98,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @mixin \Eloquent */ class CompanyGateway extends BaseModel diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index d2470281a7df..fb1c274b66d0 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -85,6 +85,9 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $token * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $token + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class CompanyUser extends Pivot diff --git a/app/Models/Credit.php b/app/Models/Credit.php index c0d8a93202be..c86dc743c9a6 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -227,6 +227,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class Credit extends BaseModel diff --git a/app/Models/Expense.php b/app/Models/Expense.php index 4408b452473f..fc92194c8dec 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -136,6 +136,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Expense extends BaseModel diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 2d0095d81a1b..0b525e6f7ba1 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -34,6 +34,7 @@ namespace App\Models; * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods + * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel diff --git a/app/Models/GroupSetting.php b/app/Models/GroupSetting.php index 8a3a1c016981..efbf46b55716 100644 --- a/app/Models/GroupSetting.php +++ b/app/Models/GroupSetting.php @@ -66,6 +66,8 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $clients * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class GroupSetting extends StaticModel diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index ea53537d276b..4ae6a4f577a2 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -259,6 +259,15 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Invoice extends BaseModel diff --git a/app/Models/Payment.php b/app/Models/Payment.php index f5bd60ca39be..6117d931b42d 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -162,6 +162,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $paymentables + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $paymentables * @mixin \Eloquent */ class Payment extends BaseModel diff --git a/app/Models/Product.php b/app/Models/Product.php index a541bc6ff45a..54f2f8a95cb1 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -104,6 +104,7 @@ use League\CommonMark\CommonMarkConverter; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Product extends BaseModel diff --git a/app/Models/Project.php b/app/Models/Project.php index 87374eff9925..2d0823c9f2fb 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -84,6 +84,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Project extends BaseModel diff --git a/app/Models/Proposal.php b/app/Models/Proposal.php index 2b8e68a085ab..d22141aa991a 100644 --- a/app/Models/Proposal.php +++ b/app/Models/Proposal.php @@ -34,6 +34,7 @@ use App\Utils\Traits\MakesHash; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Proposal extends BaseModel diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index 64a8f37c440f..d40ff825e686 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -216,6 +216,12 @@ use Illuminate\Support\Facades\Storage; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class PurchaseOrder extends BaseModel diff --git a/app/Models/Quote.php b/app/Models/Quote.php index fedf67c0a0b1..df4c252c8fbf 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -205,6 +205,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @mixin \Eloquent */ class Quote extends BaseModel diff --git a/app/Models/RecurringExpense.php b/app/Models/RecurringExpense.php index cc77cda53cb8..c197081c4fac 100644 --- a/app/Models/RecurringExpense.php +++ b/app/Models/RecurringExpense.php @@ -145,6 +145,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class RecurringExpense extends BaseModel diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index 57d72525b39f..be2b5fb7655b 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -209,6 +209,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @mixin \Eloquent */ class RecurringInvoice extends BaseModel diff --git a/app/Models/RecurringQuote.php b/app/Models/RecurringQuote.php index 4b7085afc083..c7e504eb574a 100644 --- a/app/Models/RecurringQuote.php +++ b/app/Models/RecurringQuote.php @@ -201,6 +201,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $quotes * @mixin \Eloquent */ class RecurringQuote extends BaseModel diff --git a/app/Models/Task.php b/app/Models/Task.php index d0b8669ea792..58e009b9db63 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -97,6 +97,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Task extends BaseModel diff --git a/app/Models/User.php b/app/Models/User.php index 1cd305b22ef5..0e004f313046 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -181,6 +181,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @mixin \Eloquent */ class User extends Authenticatable implements MustVerifyEmail diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 363995acf8e3..681cfa384f58 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -128,6 +128,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $contacts * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact * @mixin \Eloquent */ class Vendor extends BaseModel diff --git a/app/Models/VendorContact.php b/app/Models/VendorContact.php index 0e70270b19fd..66da46635ae4 100644 --- a/app/Models/VendorContact.php +++ b/app/Models/VendorContact.php @@ -122,6 +122,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @mixin \Eloquent */ class VendorContact extends Authenticatable implements HasLocalePreference diff --git a/app/Services/Tax/Providers/EuTax.php b/app/Services/Tax/Providers/EuTax.php index 9c29727d72b8..79cb15e2e669 100644 --- a/app/Services/Tax/Providers/EuTax.php +++ b/app/Services/Tax/Providers/EuTax.php @@ -28,7 +28,7 @@ class EuTax private float $vat_rate = 0.0; - private float $vat_reduced_rate = 0.0; + private float $reduced_vat_rate = 0.0; private array $eu_country_codes = [ 'AT', // Austria @@ -86,7 +86,7 @@ class EuTax public function getVatReducedRate(): float { - return $this->vat_reduced_rate; + return $this->reduced_vat_rate; } public function getVendorCountryCode(): string @@ -125,29 +125,41 @@ class EuTax { if( - (($this->vendor_country_code == $this->client_country_code) && $this->valid_vat_number && $this->rule->business_tax_exempt) || - (in_array($this->client_country_code, $this->eu_country_codes) && $this->valid_vat_number && $this->rule->business_tax_exempt) + (($this->vendor_country_code == $this->client_country_code) && $this->valid_vat_number && $this->rule->business_tax_exempt) || //same country / exempt for tax / valid vat number + (in_array($this->client_country_code, $this->eu_country_codes) && $this->valid_vat_number && $this->rule->eu_business_tax_exempt) //eu country / exempt for tax / valid vat number ) { - $this->vat_rate = 0.0; - $this->vat_reduced_rate = 0.0; + $this->vat_rate = 0; + $this->reduced_vat_rate = 0; + nlog("euro zone and tax exempt"); } - elseif(!in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && ($this->rule->foreign_consumer_tax_exempt || $this->rule->foreign_business_tax_exempt)) { - nlog($this->client_country_code); - $this->vat_rate = 0.0; - $this->vat_reduced_rate = 0.0; + elseif(!in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && ($this->rule->foreign_consumer_tax_exempt || $this->rule->foreign_business_tax_exempt)) //foreign + tax exempt + { + $this->vat_rate = 0; + $this->reduced_vat_rate = 0; + nlog("foreign and tax exempt"); } - elseif(in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && !$this->valid_vat_number) { - $rate_name = $this->client_country_code."_vat_rate"; - $this->vat_rate = $this->rule->{$rate_name}; - $this->vat_reduced_rate = $this->rule->vat_reduced_rate; + elseif(in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && !$this->valid_vat_number) //eu country / no valid vat + { + if(($this->vendor_country_code != $this->client_country_code) && $this->company->tax_data->regions->EU->has_sales_above_threshold) + { + $this->vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->vat_rate; + $this->reduced_vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_vat_rate; + nlog("eu zone with sales above threshold"); + } + else { + $this->vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->vat_rate; + $this->reduced_vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->reduced_vat_rate; + nlog("same eu country with"); + } } else { - $rate_name = $this->vendor_country_code."_vat_rate"; - $this->vat_rate = $this->rule->{$rate_name}; - $this->vat_reduced_rate = $this->rule->vat_reduced_rate; + nlog("default tax"); + $this->vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->vat_rate; + $this->reduced_vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->reduced_vat_rate; } return $this; } } + diff --git a/app/Transformers/ClientTransformer.php b/app/Transformers/ClientTransformer.php index c3726e467d8f..af7f3338a536 100644 --- a/app/Transformers/ClientTransformer.php +++ b/app/Transformers/ClientTransformer.php @@ -149,6 +149,7 @@ class ClientTransformer extends EntityTransformer 'number' => (string) $client->number ?: '', 'has_valid_vat_number' => (bool) $client->has_valid_vat_number, 'is_tax_exempt' => (bool) $client->is_tax_exempt, + 'tax_data' => $client->tax_data ?: '', ]; } } diff --git a/app/Transformers/CompanyTransformer.php b/app/Transformers/CompanyTransformer.php index 8571375f7d5a..979f4757d504 100644 --- a/app/Transformers/CompanyTransformer.php +++ b/app/Transformers/CompanyTransformer.php @@ -195,6 +195,7 @@ class CompanyTransformer extends EntityTransformer 'invoice_task_hours' => (bool) $company->invoice_task_hours, 'calculate_taxes' => (bool) $company->calculate_taxes, 'tax_all_products' => (bool) $company->tax_all_products, + 'tax_data' => $company->tax_data ?: '', ]; } diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index d4bd2a416061..4b77fa0fb869 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -13,10 +13,17 @@ return new class extends Migration */ public function up() { - Schema::table('clients', function (Blueprint $table) { + Schema::table('clients', function (Illuminate\Database\Schema\Blueprint $table) { $table->boolean('is_tax_exempt')->default(false); $table->boolean('has_valid_vat_number')->default(false); + $table->mediumText('tax_data')->nullable()->change(); }); + + Schema::table('companies', function (Illuminate\Database\Schema\Blueprint $table) { + $table->mediumText('tax_data')->nullable()->change(); + }); + + } /** diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index 4db56aad1cff..8f2cd0fc61e4 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -16,8 +16,9 @@ use App\Models\Client; use App\Models\Company; use Tests\MockAccountData; use App\DataMapper\Tax\de\Rule; -use App\Services\Tax\Providers\EuTax; +use App\DataMapper\Tax\TaxModel; use App\DataMapper\CompanySettings; +use App\Services\Tax\Providers\EuTax; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -48,9 +49,16 @@ class EuTaxTest extends TestCase $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 + 'settings' => $settings, + 'tax_data' => $tax_data, ]); $client = Client::factory()->create([ @@ -84,11 +92,19 @@ class EuTaxTest extends TestCase $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 + 'settings' => $settings, + 'tax_data' => $tax_data, ]); + $client = Client::factory()->create([ 'user_id' => $this->user->id, 'company_id' => $company->id, @@ -109,7 +125,7 @@ class EuTaxTest extends TestCase $this->assertEquals(21, $process->getVatRate()); - $this->assertEquals(7, $process->getVatReducedRate()); + $this->assertEquals(6, $process->getVatReducedRate()); } From d4716048624208d6e6131a59c70318ce829417ca Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 14:23:06 +1100 Subject: [PATCH 19/38] Refactor for tax structure --- app/DataMapper/Tax/BaseRule.php | 45 +++++ app/DataMapper/Tax/{de => DE}/Rule.php | 64 ++++++- app/DataMapper/Tax/RuleInterface.php | 4 + app/DataMapper/Tax/{us => US}/Rule.php | 2 +- app/Helpers/Invoice/InvoiceItemSum.php | 2 +- app/Models/Account.php | 4 + app/Models/BankAccount.php | 1 + app/Models/BankIntegration.php | 1 + app/Models/Client.php | 17 ++ app/Models/ClientContact.php | 5 + app/Models/Company.php | 44 +++++ app/Models/CompanyGateway.php | 1 + app/Models/CompanyUser.php | 3 + app/Models/Credit.php | 7 + app/Models/Expense.php | 1 + app/Models/GatewayType.php | 1 + app/Models/GroupSetting.php | 2 + app/Models/Invoice.php | 9 + app/Models/Payment.php | 5 + app/Models/Product.php | 1 + app/Models/Project.php | 2 + app/Models/Proposal.php | 1 + app/Models/PurchaseOrder.php | 6 + app/Models/Quote.php | 4 + app/Models/RecurringExpense.php | 1 + app/Models/RecurringInvoice.php | 5 + app/Models/RecurringQuote.php | 5 + app/Models/Task.php | 1 + app/Models/User.php | 7 + app/Models/Vendor.php | 4 + app/Models/VendorContact.php | 2 + app/Services/Tax/Providers/EuTax.php | 165 ------------------ app/Services/Tax/TaxService.php | 12 +- ...054758_add_client_is_exempt_from_taxes.php | 3 +- tests/Unit/Tax/EuTaxTest.php | 102 ++++++++--- 35 files changed, 340 insertions(+), 199 deletions(-) rename app/DataMapper/Tax/{de => DE}/Rule.php (52%) rename app/DataMapper/Tax/{us => US}/Rule.php (98%) delete mode 100644 app/Services/Tax/Providers/EuTax.php diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index fd7cfea6da3b..4cda526ee49e 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -18,10 +18,45 @@ class BaseRule implements RuleInterface { /** EU TAXES */ public bool $consumer_tax_exempt = false; + public bool $business_tax_exempt = true; + public bool $eu_business_tax_exempt = true; + public bool $foreign_business_tax_exempt = true; + public bool $foreign_consumer_tax_exempt = true; + + public array $eu_country_codes = [ + 'AT', // Austria + 'BE', // Belgium + 'BG', // Bulgaria + 'CY', // Cyprus + 'CZ', // Czech Republic + 'DE', // Germany + 'DK', // Denmark + 'EE', // Estonia + 'ES', // Spain + 'FI', // Finland + 'FR', // France + 'GR', // Greece + 'HR', // Croatia + 'HU', // Hungary + 'IE', // Ireland + 'IT', // Italy + 'LT', // Lithuania + 'LU', // Luxembourg + 'LV', // Latvia + 'MT', // Malta + 'NL', // Netherlands + 'PL', // Poland + 'PT', // Portugal + 'RO', // Romania + 'SE', // Sweden + 'SI', // Slovenia + 'SK', // Slovakia + ]; + /** EU TAXES */ @@ -45,6 +80,11 @@ class BaseRule implements RuleInterface { } + public function init(): self + { + return $this; + } + public function setClient(Client $client): self { $this->client = $client; @@ -108,4 +148,9 @@ class BaseRule implements RuleInterface { return $this; } + + public function calculateRates(): self + { + return $this; + } } diff --git a/app/DataMapper/Tax/de/Rule.php b/app/DataMapper/Tax/DE/Rule.php similarity index 52% rename from app/DataMapper/Tax/de/Rule.php rename to app/DataMapper/Tax/DE/Rule.php index 54f33978ef07..cd1b032aae4b 100644 --- a/app/DataMapper/Tax/de/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -9,16 +9,20 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\DataMapper\Tax\de; +namespace App\DataMapper\Tax\DE; use App\Models\Client; use App\Models\Product; +use Illuminate\Support\Str; use App\DataMapper\Tax\BaseRule; use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\ZipTax\Response; class Rule extends BaseRule implements RuleInterface { + public string $vendor_country_code = 'DE'; + + public string $client_country_code = 'DE'; public bool $consumer_tax_exempt = false; @@ -41,7 +45,11 @@ class Rule extends BaseRule implements RuleInterface public string $tax_name3 = ''; public float $tax_rate3 = 0; - + + public float $vat_rate = 0; + + public float $reduced_vat_rate = 0; + protected ?Client $client; protected ?Response $tax_data; @@ -50,6 +58,14 @@ class Rule extends BaseRule implements RuleInterface { } + public function init(): self + { + $this->client_country_code = $this->client->shipping_country ? $this->client->shipping_country->iso_3166_2 : $this->client->country->iso_3166_2; + $this->calculateRates(); + + return $this; + } + public function setClient(Client $client): self { $this->client = $client; @@ -103,7 +119,7 @@ class Rule extends BaseRule implements RuleInterface public function taxReduced(): self { - $this->tax_rate1 = $this->vat_reduced_rate; + $this->tax_rate1 = $this->reduced_vat_rate; $this->tax_name1 = 'ermäßigte MwSt.'; return $this; @@ -159,4 +175,46 @@ class Rule extends BaseRule implements RuleInterface return $this; } + public function calculateRates(): self + { + + if( + (($this->vendor_country_code == $this->client_country_code) && $this->client->has_valid_vat_number && $this->business_tax_exempt) || //same country / exempt for tax / valid vat number + (in_array($this->client_country_code, $this->eu_country_codes) && $this->client->has_valid_vat_number && $this->eu_business_tax_exempt) //eu country / exempt for tax / valid vat number + ) { + $this->vat_rate = 0; + $this->reduced_vat_rate = 0; + 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"); + } + elseif(in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && !$this->client->has_valid_vat_number) //eu country / no valid vat + { + if(($this->vendor_country_code != $this->client_country_code) && $this->client->company->tax_data->regions->EU->has_sales_above_threshold) + { + $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"); + } + 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"); + } + } + 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; + } + + return $this; + + } + + } diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index 8ee20a3866a1..91c5f6ed113a 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -16,6 +16,8 @@ use App\DataMapper\Tax\ZipTax\Response; interface RuleInterface { + public function init(); + public function tax(); public function taxByType(?int $type); @@ -39,4 +41,6 @@ interface RuleInterface public function setClient(Client $client); public function setTaxData(Response $tax_data); + + public function calculateRates(); } \ No newline at end of file diff --git a/app/DataMapper/Tax/us/Rule.php b/app/DataMapper/Tax/US/Rule.php similarity index 98% rename from app/DataMapper/Tax/us/Rule.php rename to app/DataMapper/Tax/US/Rule.php index c465b7a82a87..5531a341cb24 100644 --- a/app/DataMapper/Tax/us/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -9,7 +9,7 @@ * @license https://www.elastic.co/licensing/elastic-license */ -namespace App\DataMapper\Tax\us; +namespace App\DataMapper\Tax\US; use App\Models\Client; use App\Models\Product; diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 6e859b2de5a9..6070e176f3ee 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -199,7 +199,7 @@ class InvoiceItemSum if ($this->invoice->company->tax_all_products || $this->item->tax_id != '') { $this->rule->tax(); } else { - $this->rule->taxByType($this->item->tax_id); + $this->rule->init()->taxByType($this->item->tax_id); } $this->item->tax_name1 = $this->rule->tax_name1; diff --git a/app/Models/Account.php b/app/Models/Account.php index a603a2582c4e..90c3a8b23589 100644 --- a/app/Models/Account.php +++ b/app/Models/Account.php @@ -158,6 +158,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $companies * @property-read \Illuminate\Database\Eloquent\Collection $company_users * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class Account extends BaseModel diff --git a/app/Models/BankAccount.php b/app/Models/BankAccount.php index 0762baf121dc..cf20fc461d3c 100644 --- a/app/Models/BankAccount.php +++ b/app/Models/BankAccount.php @@ -39,6 +39,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts + * @property-read \Illuminate\Database\Eloquent\Collection $bank_subaccounts * @mixin \Eloquent */ class BankAccount extends BaseModel diff --git a/app/Models/BankIntegration.php b/app/Models/BankIntegration.php index e5908e34a506..82d2d7200264 100644 --- a/app/Models/BankIntegration.php +++ b/app/Models/BankIntegration.php @@ -82,6 +82,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @property-read \Illuminate\Database\Eloquent\Collection $transactions + * @property-read \Illuminate\Database\Eloquent\Collection $transactions * @mixin \Eloquent */ class BankIntegration extends BaseModel diff --git a/app/Models/Client.php b/app/Models/Client.php index 36286c79229c..e4907deaf4a8 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -291,6 +291,23 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices * @property-read \Illuminate\Database\Eloquent\Collection $system_logs * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Client extends BaseModel implements HasLocalePreference diff --git a/app/Models/ClientContact.php b/app/Models/ClientContact.php index 4060c4588c81..361ab3101e73 100644 --- a/app/Models/ClientContact.php +++ b/app/Models/ClientContact.php @@ -158,6 +158,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $credit_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoice_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $quote_invitations + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoice_invitations * @mixin \Eloquent */ class ClientContact extends Authenticatable implements HasLocalePreference diff --git a/app/Models/Company.php b/app/Models/Company.php index 8abf25eaf6cd..6a71b7d519ce 100644 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -596,6 +596,50 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $users * @property-read \Illuminate\Database\Eloquent\Collection $vendors * @property-read \Illuminate\Database\Eloquent\Collection $webhooks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_activities + * @property-read \Illuminate\Database\Eloquent\Collection $all_documents + * @property-read \Illuminate\Database\Eloquent\Collection $bank_integrations + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transaction_rules + * @property-read \Illuminate\Database\Eloquent\Collection $bank_transactions + * @property-read \Illuminate\Database\Eloquent\Collection $client_contacts + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $company_gateways + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $designs + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expense_categories + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $group_settings + * @property-read \Illuminate\Database\Eloquent\Collection $groups + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $ledger + * @property-read \Illuminate\Database\Eloquent\Collection $payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $products + * @property-read \Illuminate\Database\Eloquent\Collection $projects + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_orders + * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_expenses + * @property-read \Illuminate\Database\Eloquent\Collection $recurring_invoices + * @property-read \Illuminate\Database\Eloquent\Collection $schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $subscriptions + * @property-read \Illuminate\Database\Eloquent\Collection $system_log_relation + * @property-read \Illuminate\Database\Eloquent\Collection $system_logs + * @property-read \Illuminate\Database\Eloquent\Collection $task_schedulers + * @property-read \Illuminate\Database\Eloquent\Collection $task_statuses + * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $tax_rates + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $tokens_hashed + * @property-read \Illuminate\Database\Eloquent\Collection $user_designs + * @property-read \Illuminate\Database\Eloquent\Collection $user_payment_terms + * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $vendors + * @property-read \Illuminate\Database\Eloquent\Collection $webhooks * @mixin \Eloquent */ class Company extends BaseModel diff --git a/app/Models/CompanyGateway.php b/app/Models/CompanyGateway.php index 4b51a9d91fc9..b19b65d98071 100644 --- a/app/Models/CompanyGateway.php +++ b/app/Models/CompanyGateway.php @@ -99,6 +99,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens + * @property-read \Illuminate\Database\Eloquent\Collection $client_gateway_tokens * @mixin \Eloquent */ class CompanyGateway extends BaseModel diff --git a/app/Models/CompanyUser.php b/app/Models/CompanyUser.php index fb1c274b66d0..b88e459c54ab 100644 --- a/app/Models/CompanyUser.php +++ b/app/Models/CompanyUser.php @@ -88,6 +88,9 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $token * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @property-read \Illuminate\Database\Eloquent\Collection $users + * @property-read \Illuminate\Database\Eloquent\Collection $token + * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $users * @mixin \Eloquent */ class CompanyUser extends Pivot diff --git a/app/Models/Credit.php b/app/Models/Credit.php index c86dc743c9a6..5f3255a1df57 100644 --- a/app/Models/Credit.php +++ b/app/Models/Credit.php @@ -234,6 +234,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class Credit extends BaseModel diff --git a/app/Models/Expense.php b/app/Models/Expense.php index fc92194c8dec..f4a48d1cf35a 100644 --- a/app/Models/Expense.php +++ b/app/Models/Expense.php @@ -137,6 +137,7 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Expense extends BaseModel diff --git a/app/Models/GatewayType.php b/app/Models/GatewayType.php index 0b525e6f7ba1..98a9e6e71439 100644 --- a/app/Models/GatewayType.php +++ b/app/Models/GatewayType.php @@ -35,6 +35,7 @@ namespace App\Models; * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods + * @property-read \Illuminate\Database\Eloquent\Collection $payment_methods * @mixin \Eloquent */ class GatewayType extends StaticModel diff --git a/app/Models/GroupSetting.php b/app/Models/GroupSetting.php index efbf46b55716..b736b4c6a72e 100644 --- a/app/Models/GroupSetting.php +++ b/app/Models/GroupSetting.php @@ -68,6 +68,8 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $clients * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class GroupSetting extends StaticModel diff --git a/app/Models/Invoice.php b/app/Models/Invoice.php index 4ae6a4f577a2..ffce822140e2 100644 --- a/app/Models/Invoice.php +++ b/app/Models/Invoice.php @@ -268,6 +268,15 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $payments * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $expenses + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Invoice extends BaseModel diff --git a/app/Models/Payment.php b/app/Models/Payment.php index 6117d931b42d..aed3fbc295d2 100644 --- a/app/Models/Payment.php +++ b/app/Models/Payment.php @@ -167,6 +167,11 @@ use Illuminate\Database\Eloquent\SoftDeletes; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $paymentables + * @property-read \Illuminate\Database\Eloquent\Collection $company_ledger + * @property-read \Illuminate\Database\Eloquent\Collection $credits + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $paymentables * @mixin \Eloquent */ class Payment extends BaseModel diff --git a/app/Models/Product.php b/app/Models/Product.php index 54f2f8a95cb1..b04767ae6146 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -105,6 +105,7 @@ use League\CommonMark\CommonMarkConverter; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Product extends BaseModel diff --git a/app/Models/Project.php b/app/Models/Project.php index 2d0823c9f2fb..a4c837c69f80 100644 --- a/app/Models/Project.php +++ b/app/Models/Project.php @@ -86,6 +86,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $tasks + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $tasks * @mixin \Eloquent */ class Project extends BaseModel diff --git a/app/Models/Proposal.php b/app/Models/Proposal.php index d22141aa991a..7b86e0ed4b9c 100644 --- a/app/Models/Proposal.php +++ b/app/Models/Proposal.php @@ -35,6 +35,7 @@ use App\Utils\Traits\MakesHash; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Proposal extends BaseModel diff --git a/app/Models/PurchaseOrder.php b/app/Models/PurchaseOrder.php index d40ff825e686..c44cf6abc865 100644 --- a/app/Models/PurchaseOrder.php +++ b/app/Models/PurchaseOrder.php @@ -222,6 +222,12 @@ use Illuminate\Support\Facades\Storage; * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @property-read \Illuminate\Database\Eloquent\Collection $payments + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $payments * @mixin \Eloquent */ class PurchaseOrder extends BaseModel diff --git a/app/Models/Quote.php b/app/Models/Quote.php index df4c252c8fbf..1c0940c88d41 100644 --- a/app/Models/Quote.php +++ b/app/Models/Quote.php @@ -209,6 +209,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @mixin \Eloquent */ class Quote extends BaseModel diff --git a/app/Models/RecurringExpense.php b/app/Models/RecurringExpense.php index c197081c4fac..f96a0d9a85a4 100644 --- a/app/Models/RecurringExpense.php +++ b/app/Models/RecurringExpense.php @@ -146,6 +146,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class RecurringExpense extends BaseModel diff --git a/app/Models/RecurringInvoice.php b/app/Models/RecurringInvoice.php index be2b5fb7655b..3667b25583f3 100644 --- a/app/Models/RecurringInvoice.php +++ b/app/Models/RecurringInvoice.php @@ -214,6 +214,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $invoices + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $invoices * @mixin \Eloquent */ class RecurringInvoice extends BaseModel diff --git a/app/Models/RecurringQuote.php b/app/Models/RecurringQuote.php index c7e504eb574a..4759f90f5660 100644 --- a/app/Models/RecurringQuote.php +++ b/app/Models/RecurringQuote.php @@ -206,6 +206,11 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $history * @property-read \Illuminate\Database\Eloquent\Collection $invitations * @property-read \Illuminate\Database\Eloquent\Collection $quotes + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $history + * @property-read \Illuminate\Database\Eloquent\Collection $invitations + * @property-read \Illuminate\Database\Eloquent\Collection $quotes * @mixin \Eloquent */ class RecurringQuote extends BaseModel diff --git a/app/Models/Task.php b/app/Models/Task.php index 58e009b9db63..70e00f1ac34f 100644 --- a/app/Models/Task.php +++ b/app/Models/Task.php @@ -98,6 +98,7 @@ use Illuminate\Support\Carbon; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $documents * @mixin \Eloquent */ class Task extends BaseModel diff --git a/app/Models/User.php b/app/Models/User.php index 0e004f313046..ea4185e21620 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -188,6 +188,13 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $tokens + * @property-read \Illuminate\Database\Eloquent\Collection $clients + * @property-read \Illuminate\Database\Eloquent\Collection $companies + * @property-read \Illuminate\Database\Eloquent\Collection $company_users + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $tokens * @mixin \Eloquent */ class User extends Authenticatable implements MustVerifyEmail diff --git a/app/Models/Vendor.php b/app/Models/Vendor.php index 681cfa384f58..4c8f916729d0 100644 --- a/app/Models/Vendor.php +++ b/app/Models/Vendor.php @@ -132,6 +132,10 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $contacts * @property-read \Illuminate\Database\Eloquent\Collection $documents * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact + * @property-read \Illuminate\Database\Eloquent\Collection $activities + * @property-read \Illuminate\Database\Eloquent\Collection $contacts + * @property-read \Illuminate\Database\Eloquent\Collection $documents + * @property-read \Illuminate\Database\Eloquent\Collection $primary_contact * @mixin \Eloquent */ class Vendor extends BaseModel diff --git a/app/Models/VendorContact.php b/app/Models/VendorContact.php index 66da46635ae4..6f8fec742068 100644 --- a/app/Models/VendorContact.php +++ b/app/Models/VendorContact.php @@ -124,6 +124,8 @@ use Laracasts\Presenter\PresentableTrait; * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations + * @property-read \Illuminate\Notifications\DatabaseNotificationCollection $notifications + * @property-read \Illuminate\Database\Eloquent\Collection $purchase_order_invitations * @mixin \Eloquent */ class VendorContact extends Authenticatable implements HasLocalePreference diff --git a/app/Services/Tax/Providers/EuTax.php b/app/Services/Tax/Providers/EuTax.php deleted file mode 100644 index 79cb15e2e669..000000000000 --- a/app/Services/Tax/Providers/EuTax.php +++ /dev/null @@ -1,165 +0,0 @@ -setUp() - ->validateVat() - ->calculateVatRates(); - } - - public function hasValidVatNumber(): bool - { - return $this->valid_vat_number; - } - - public function getVatRate(): float - { - return $this->vat_rate; - } - - public function getVatReducedRate(): float - { - return $this->reduced_vat_rate; - } - - public function getVendorCountryCode(): string - { - return $this->vendor_country_code; - } - - public function getClientCountryCode(): string - { - return $this->client_country_code; - } - - private function setUp(): self - { - $this->vendor_country_code = Str::lower($this->company->country()->iso_3166_2); - - $this->client_country_code = $this->client->shipping_country ? Str::lower($this->client->shipping_country->iso_3166_2) : Str::lower($this->client->country->iso_3166_2); - - $class = "App\\DataMapper\\Tax\\".$this->vendor_country_code."\\Rule"; - - $this->rule = new $class(); - - return $this; - } - - private function validateVat(): self - { - $vat_check = (new VatNumberCheck($this->client->vat_number, $this->client_country_code))->run(); - - $this->valid_vat_number = $vat_check->isValid(); - - return $this; - } - - private function calculateVatRates(): self - { - - if( - (($this->vendor_country_code == $this->client_country_code) && $this->valid_vat_number && $this->rule->business_tax_exempt) || //same country / exempt for tax / valid vat number - (in_array($this->client_country_code, $this->eu_country_codes) && $this->valid_vat_number && $this->rule->eu_business_tax_exempt) //eu country / exempt for tax / valid vat number - ) { - $this->vat_rate = 0; - $this->reduced_vat_rate = 0; - nlog("euro zone and tax exempt"); - } - elseif(!in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && ($this->rule->foreign_consumer_tax_exempt || $this->rule->foreign_business_tax_exempt)) //foreign + tax exempt - { - $this->vat_rate = 0; - $this->reduced_vat_rate = 0; - nlog("foreign and tax exempt"); - } - elseif(in_array(strtoupper($this->client_country_code), $this->eu_country_codes) && !$this->valid_vat_number) //eu country / no valid vat - { - if(($this->vendor_country_code != $this->client_country_code) && $this->company->tax_data->regions->EU->has_sales_above_threshold) - { - $this->vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->vat_rate; - $this->reduced_vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->client->country->iso_3166_2}->reduced_vat_rate; - nlog("eu zone with sales above threshold"); - } - else { - $this->vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->vat_rate; - $this->reduced_vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->reduced_vat_rate; - nlog("same eu country with"); - } - } - else { - nlog("default tax"); - $this->vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->vat_rate; - $this->reduced_vat_rate = $this->company->tax_data->regions->EU->subregions->{$this->company->country()->iso_3166_2}->reduced_vat_rate; - } - - return $this; - - } -} - diff --git a/app/Services/Tax/TaxService.php b/app/Services/Tax/TaxService.php index 678418c240b5..354681502bb7 100644 --- a/app/Services/Tax/TaxService.php +++ b/app/Services/Tax/TaxService.php @@ -23,5 +23,15 @@ class TaxService { } - + private function validateVat(): self + { + $client_country_code = $this->client->shipping_country ? $this->client->shipping_country->iso_3166_2 : $this->client->country->iso_3166_2; + + $vat_check = (new VatNumberCheck($this->client->vat_number, $client_country_code))->run(); + + $this->client->has_valid_vat_number = $vat_check->isValid(); + $this->client->saveQuietly(); + + return $this; + } } \ No newline at end of file diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index 4b77fa0fb869..b298eeb46708 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -22,8 +22,7 @@ return new class extends Migration Schema::table('companies', function (Illuminate\Database\Schema\Blueprint $table) { $table->mediumText('tax_data')->nullable()->change(); }); - - + } /** diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index 8f2cd0fc61e4..995844ac10b1 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -15,10 +15,9 @@ use Tests\TestCase; use App\Models\Client; use App\Models\Company; use Tests\MockAccountData; -use App\DataMapper\Tax\de\Rule; +use App\DataMapper\Tax\DE\Rule; use App\DataMapper\Tax\TaxModel; use App\DataMapper\CompanySettings; -use App\Services\Tax\Providers\EuTax; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -66,22 +65,24 @@ class EuTaxTest extends TestCase 'company_id' => $company->id, 'country_id' => 276, 'shipping_country_id' => 276, + 'has_valid_vat_number' => false, ]); - $process = new EuTax($company, $client); - $process->run(); + $process = new Rule(); + $process->setClient($client); + $process->init(); - $this->assertEquals('de', $process->getVendorCountryCode()); + $this->assertEquals('DE', $process->vendor_country_code); - $this->assertEquals('de', $process->getClientCountryCode()); + $this->assertEquals('DE', $process->client_country_code); - $this->assertFalse($process->hasValidVatNumber()); + $this->assertFalse($client->has_valid_vat_number); - $this->assertInstanceOf(Rule::class, $process->rule); + $this->assertInstanceOf(Rule::class, $process); - $this->assertEquals(19, $process->getVatRate()); + $this->assertEquals(19, $process->vat_rate); - $this->assertEquals(7, $process->getVatReducedRate()); + $this->assertEquals(7, $process->reduced_vat_rate); } @@ -110,22 +111,25 @@ class EuTaxTest extends TestCase 'company_id' => $company->id, 'country_id' => 56, 'shipping_country_id' => 56, + 'has_valid_vat_number' => false, ]); - $process = new EuTax($company, $client); - $process->run(); + $process = new Rule(); + $process->setClient($client); + $process->init(); - $this->assertEquals('de', $process->getVendorCountryCode()); - $this->assertEquals('be', $process->getClientCountryCode()); + $this->assertEquals('DE', $process->vendor_country_code); - $this->assertFalse($process->hasValidVatNumber()); + $this->assertEquals('BE', $process->client_country_code); - $this->assertInstanceOf(Rule::class, $process->rule); + $this->assertFalse($client->has_valid_vat_number); - $this->assertEquals(21, $process->getVatRate()); + $this->assertInstanceOf(Rule::class, $process); - $this->assertEquals(6, $process->getVatReducedRate()); + $this->assertEquals(21, $process->vat_rate); + + $this->assertEquals(6, $process->reduced_vat_rate); } @@ -146,25 +150,71 @@ class EuTaxTest extends TestCase 'company_id' => $company->id, 'country_id' => 840, 'shipping_country_id' => 840, + 'has_valid_vat_number' => false, ]); - $process = new EuTax($company, $client); - $process->run(); + $process = new Rule(); + $process->setClient($client); + $process->init(); - $this->assertEquals('de', $process->getVendorCountryCode()); - $this->assertEquals('us', $process->getClientCountryCode()); + $this->assertEquals('DE', $process->vendor_country_code); - $this->assertFalse($process->hasValidVatNumber()); + $this->assertEquals('US', $process->client_country_code); - $this->assertInstanceOf(Rule::class, $process->rule); + $this->assertFalse($client->has_valid_vat_number); - $this->assertEquals(0, $process->getVatRate()); + $this->assertInstanceOf(Rule::class, $process); - $this->assertEquals(0, $process->getVatReducedRate()); + $this->assertEquals(0, $process->vat_rate); + + $this->assertEquals(0, $process->reduced_vat_rate); } + public function testSubThresholdCorrectRate() + { + + $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 = false; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + ]); + + + $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, + ]); + + $process = new Rule(); + $process->setClient($client); + $process->init(); + + $this->assertInstanceOf(Rule::class, $process); + + $this->assertFalse($client->has_valid_vat_number); + + $this->assertEquals(19, $process->vat_rate); + + $this->assertEquals(7, $process->reduced_vat_rate); + + } + + + //tests with valid vat. } From 45632721a210b18725a39d0f51fc4eacd12cb713 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 14:30:16 +1100 Subject: [PATCH 20/38] Refactor for tax structure --- app/DataMapper/Tax/US/Rule.php | 10 ++++++++++ app/Helpers/Invoice/InvoiceItemSum.php | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index 5531a341cb24..d6dde1a5e835 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -144,4 +144,14 @@ class Rule implements RuleInterface return $this; } + + public function init(): self + { + return $this; + } + + public function calculateRates(): self + { + return $this; + } } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 6070e176f3ee..adc8d562039e 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -141,7 +141,7 @@ 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\\".strtolower($this->client->country->iso_3166_2)."\\Rule"; + $class = "App\DataMapper\Tax\\".$this->client->country->iso_3166_2."\\Rule"; $tax_data = new Response($this->invoice->tax_data); From aa91604814deb4f69d4ba3a04e16711a4970ca05 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 14:42:08 +1100 Subject: [PATCH 21/38] Fixes for eu taxes --- app/DataMapper/Tax/DE/Rule.php | 12 +- tests/Unit/Tax/EuTaxTest.php | 204 ++++++++++++++++++++++++++++++++- 2 files changed, 210 insertions(+), 6 deletions(-) diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index cd1b032aae4b..56a388dfea6d 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -83,7 +83,7 @@ class Rule extends BaseRule implements RuleInterface //need to add logic here to capture if public function tax(): self { - if($this->client->is_tax_exempt || $this->client->has_valid_vat_number) + if($this->client->is_tax_exempt) return $this->taxExempt(); $this->tax_name1 = $this->vat_rate; @@ -178,10 +178,12 @@ class Rule extends BaseRule implements RuleInterface public function calculateRates(): self { - if( - (($this->vendor_country_code == $this->client_country_code) && $this->client->has_valid_vat_number && $this->business_tax_exempt) || //same country / exempt for tax / valid vat number - (in_array($this->client_country_code, $this->eu_country_codes) && $this->client->has_valid_vat_number && $this->eu_business_tax_exempt) //eu country / exempt for tax / valid vat number - ) { + if ($this->client->is_tax_exempt) { + $this->vat_rate = 0; + $this->reduced_vat_rate = 0; + } + elseif($this->client_country_code != $this->vendor_country_code && in_array($this->client_country_code, $this->eu_country_codes) && $this->client->has_valid_vat_number && $this->eu_business_tax_exempt) + { $this->vat_rate = 0; $this->reduced_vat_rate = 0; nlog("euro zone and tax exempt"); diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index 995844ac10b1..6f8676cd14f7 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -157,7 +157,6 @@ class EuTaxTest extends TestCase $process->setClient($client); $process->init(); - $this->assertEquals('DE', $process->vendor_country_code); $this->assertEquals('US', $process->client_country_code); @@ -216,5 +215,208 @@ class EuTaxTest extends TestCase //tests with valid vat. + public function testDeWithValidVat() + { + $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 = false; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + ]); + + + $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, + ]); + + $process = new Rule(); + $process->setClient($client); + $process->init(); + + $this->assertInstanceOf(Rule::class, $process); + + $this->assertTrue($client->has_valid_vat_number); + + $this->assertEquals(19, $process->vat_rate); + + $this->assertEquals(7, $process->reduced_vat_rate); + + } + + //tests with valid vat. + public function testDeToEUWithValidVat() + { + $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 = false; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + ]); + + + $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, + ]); + + $process = new Rule(); + $process->setClient($client); + $process->init(); + + $this->assertInstanceOf(Rule::class, $process); + + $this->assertTrue($client->has_valid_vat_number); + + $this->assertEquals(0, $process->vat_rate); + + $this->assertEquals(0, $process->reduced_vat_rate); + + } + + public function testTaxExemption1() + { + $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 = false; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + ]); + + + $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, + 'is_tax_exempt' => true, + ]); + + $process = new Rule(); + $process->setClient($client); + $process->init(); + + $this->assertInstanceOf(Rule::class, $process); + + $this->assertTrue($client->is_tax_exempt); + + $this->assertEquals(0, $process->vat_rate); + + $this->assertEquals(0, $process->reduced_vat_rate); + + } + + public function testTaxExemption2() + { + $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 = false; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + ]); + + + $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, + 'is_tax_exempt' => true, + ]); + + $process = new Rule(); + $process->setClient($client); + $process->init(); + + $this->assertInstanceOf(Rule::class, $process); + + $this->assertTrue($client->is_tax_exempt); + + $this->assertEquals(0, $process->vat_rate); + + $this->assertEquals(0, $process->reduced_vat_rate); + + } + + public function testTaxExemption3() + { + $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 = false; + $tax_data->regions->EU->tax_all = true; + + $company = Company::factory()->create([ + 'account_id' => $this->account->id, + 'settings' => $settings, + 'tax_data' => $tax_data, + ]); + + + $client = Client::factory()->create([ + 'user_id' => $this->user->id, + 'company_id' => $company->id, + 'country_id' => 840, + 'shipping_country_id' => 840, + 'has_valid_vat_number' => true, + 'is_tax_exempt' => true, + ]); + + $process = new Rule(); + $process->setClient($client); + $process->init(); + + $this->assertInstanceOf(Rule::class, $process); + + $this->assertTrue($client->is_tax_exempt); + + $this->assertEquals(0, $process->vat_rate); + + $this->assertEquals(0, $process->reduced_vat_rate); + + } } From 0bb3ea6d074c44d294eb54a79e6216f616bec8ec Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 14:45:59 +1100 Subject: [PATCH 22/38] Fixes for eu taxes --- app/DataMapper/Tax/ZipTax/Response.php | 7 ------- 1 file changed, 7 deletions(-) diff --git a/app/DataMapper/Tax/ZipTax/Response.php b/app/DataMapper/Tax/ZipTax/Response.php index 8ea3d7d4b307..3e4893cdd28c 100644 --- a/app/DataMapper/Tax/ZipTax/Response.php +++ b/app/DataMapper/Tax/ZipTax/Response.php @@ -95,13 +95,6 @@ class Response public float $district5UseTax = 0; public string $originDestination = ""; - //EU - public float $vat_rate = 0; - public float $vat_reduced_rate = 0; - public string $vat_country_code = ""; - - - public function __construct($data) { From 00c7fd7d37a4d8d4aeb9f95bea434258e2466898 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 20:49:40 +1100 Subject: [PATCH 23/38] Tests for calculating invoice taxes --- app/DataMapper/Tax/DE/Rule.php | 14 +- app/Helpers/Invoice/InvoiceItemSum.php | 13 +- tests/Unit/Tax/EuTaxTest.php | 202 ++++++++++++++++++++++++- 3 files changed, 215 insertions(+), 14 deletions(-) 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 From 1c8cccdaf3ff89fdb01ee44d6d21d66845e203d0 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Wed, 29 Mar 2023 23:39:50 +1100 Subject: [PATCH 24/38] Remove GB from eu countries --- app/DataMapper/Tax/TaxModel.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/DataMapper/Tax/TaxModel.php b/app/DataMapper/Tax/TaxModel.php index 99b85764451c..f127759d69cc 100644 --- a/app/DataMapper/Tax/TaxModel.php +++ b/app/DataMapper/Tax/TaxModel.php @@ -229,10 +229,10 @@ class TaxModel $this->regions->EU->subregions->FR->reduced_vat_rate = 5.5; $this->regions->EU->subregions->FR->apply_tax = false; - $this->regions->EU->subregions->GB = new \stdClass(); - $this->regions->EU->subregions->GB->vat_rate = 20; - $this->regions->EU->subregions->GB->reduced_vat_rate = 0; - $this->regions->EU->subregions->GB->apply_tax = false; + // $this->regions->EU->subregions->GB = new \stdClass(); + // $this->regions->EU->subregions->GB->vat_rate = 20; + // $this->regions->EU->subregions->GB->reduced_vat_rate = 0; + // $this->regions->EU->subregions->GB->apply_tax = false; $this->regions->EU->subregions->GR = new \stdClass(); $this->regions->EU->subregions->GR->vat_rate = 24; From 90e1d8d2284b846b1eec0207c4d9cd30f86747fa Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 31 Mar 2023 15:12:00 +1100 Subject: [PATCH 25/38] US Tax tests --- tests/Unit/Tax/EuTaxTest.php | 4 + tests/Unit/Tax/UsTaxTest.php | 190 +++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+) create mode 100644 tests/Unit/Tax/UsTaxTest.php diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index f4504c4b35e4..1559ae3c8d7d 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -44,6 +44,10 @@ class EuTaxTest extends TestCase $this->makeTestData(); } + public function testInvoiceWithCustomTaxIdTax() + { + + } public function testInvoiceTaxCalcDetoBeNoVat() { diff --git a/tests/Unit/Tax/UsTaxTest.php b/tests/Unit/Tax/UsTaxTest.php new file mode 100644 index 000000000000..75551b316630 --- /dev/null +++ b/tests/Unit/Tax/UsTaxTest.php @@ -0,0 +1,190 @@ + "92582", + "geoCity" => "SAN JACINTO", + "geoCounty" => "RIVERSIDE", + "geoState" => "CA", + "taxSales" => 0.0875, + "taxUse" => 0.0875, + "txbService" => "N", + "txbFreight" => "N", + "stateSalesTax" => 0.06, + "stateUseTax" => 0.06, + "citySalesTax" => 0.01, + "cityUseTax" => 0.01, + "cityTaxCode" => "874", + "countySalesTax" => 0.0025, + "countyUseTax" => 0.0025, + "countyTaxCode" => "", + "districtSalesTax" => 0.015, + "districtUseTax" => 0.015, + "district1Code" => "26", + "district1SalesTax" => 0, + "district1UseTax" => 0, + "district2Code" => "26", + "district2SalesTax" => 0.005, + "district2UseTax" => 0.005, + "district3Code" => "", + "district3SalesTax" => 0, + "district3UseTax" => 0, + "district4Code" => "33", + "district4SalesTax" => 0.01, + "district4UseTax" => 0.01, + "district5Code" => "", + "district5SalesTax" => 0, + "district5UseTax" => 0, + "originDestination" => "D", + ]; + + protected function setUp(): void + { + parent::setUp(); + + $this->withoutMiddleware( + ThrottleRequests::class + ); + + $this->withoutExceptionHandling(); + + $this->makeTestData(); + } + + private function invoiceStub(?string $postal_code = '') + { + + $settings = CompanySettings::defaults(); + $settings->country_id = '840'; // germany + + $tax_data = new TaxModel(); + $tax_data->seller_region = 'US'; + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->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' => 840, + 'shipping_country_id' => 840, + 'has_valid_vat_number' => false, + 'postal_code' => $postal_code, + ]); + + $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($this->mock_response), + ]); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + return $invoice; + } + + public function testHasValidVatMakesNoDifferenceToTaxCalc() + { + + $invoice = $this->invoiceStub('92582'); + $client = $invoice->client; + $client->has_valid_vat_number = true; + $client->save(); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(8.75, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(108.75, $invoice->amount); + + + } + + public function testTaxExemption() + { + $invoice = $this->invoiceStub('92582'); + $client = $invoice->client; + $client->is_tax_exempt = true; + $client->save(); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(0, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(100, $invoice->amount); + } + + public function testBasicTaxCalculation() + { + + $invoice = $this->invoiceStub(); + + + $this->assertEquals(8.75, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(108.75, $invoice->amount); + + + } + +} From fec69f98e24b8969b8724381196867172ecfa0b8 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Fri, 31 Mar 2023 15:25:30 +1100 Subject: [PATCH 26/38] Working on tax all settings --- app/DataMapper/Tax/BaseRule.php | 2 +- app/DataMapper/Tax/DE/Rule.php | 2 +- app/DataMapper/Tax/RuleInterface.php | 2 +- app/DataMapper/Tax/US/Rule.php | 2 +- tests/Unit/Tax/EuTaxTest.php | 5 -- tests/Unit/Tax/UsTaxTest.php | 104 ++++++++++++++++++++++++++- 6 files changed, 106 insertions(+), 11 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 4cda526ee49e..26d01eb57f7d 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -104,7 +104,7 @@ class BaseRule implements RuleInterface return $this; } - public function taxByType(?int $product_tax_type): self + public function taxByType($product_tax_type): self { return $this; } diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index 3b9a2d54fe6c..50d2c0d48a57 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -93,7 +93,7 @@ class Rule extends BaseRule implements RuleInterface } - public function taxByType(?int $product_tax_type): self + public function taxByType($product_tax_type): self { if ($this->client->is_tax_exempt) { diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index 91c5f6ed113a..3ed6f8d06e4f 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -20,7 +20,7 @@ interface RuleInterface public function tax(); - public function taxByType(?int $type); + public function taxByType($type); public function taxExempt(); diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index d6dde1a5e835..5be5aab6ddda 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -67,7 +67,7 @@ class Rule implements RuleInterface } - public function taxByType(?int $product_tax_type): self + public function taxByType($product_tax_type): self { if(!$product_tax_type) return $this; diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index 1559ae3c8d7d..52ac72ac8c8e 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -44,11 +44,6 @@ class EuTaxTest extends TestCase $this->makeTestData(); } - public function testInvoiceWithCustomTaxIdTax() - { - - } - public function testInvoiceTaxCalcDetoBeNoVat() { $settings = CompanySettings::defaults(); diff --git a/tests/Unit/Tax/UsTaxTest.php b/tests/Unit/Tax/UsTaxTest.php index 75551b316630..e5791ef9fdab 100644 --- a/tests/Unit/Tax/UsTaxTest.php +++ b/tests/Unit/Tax/UsTaxTest.php @@ -146,6 +146,107 @@ class UsTaxTest extends TestCase return $invoice; } + // public function testCompanyTaxAllOffTaxExemptProduct() + // { + + // $invoice = $this->invoiceStub('92582'); + // $client = $invoice->client; + // $client->is_tax_exempt = false; + // $client->save(); + + // $company = $invoice->company; + // $tax_data = $company->tax_data; + + // $tax_data->regions->US->has_sales_above_threshold = true; + // $tax_data->regions->US->tax_all = false; + + // $company->tax_data = $tax_data; + // $company->save(); + + // $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + // $this->assertEquals(0, $invoice->line_items[0]->tax_rate1); + // $this->assertEquals(100, $invoice->amount); + + // } + + public function testCompanyTaxAllOffButTaxUSRegion() + { + + $invoice = $this->invoiceStub('92582'); + $client = $invoice->client; + $client->is_tax_exempt = false; + $client->save(); + + $company = $invoice->company; + $company->tax_all_products = false; + $tax_data = $company->tax_data; + + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all = true; + + $company->tax_data = $tax_data; + $company->save(); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(8.75, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(108.75, $invoice->amount); + + } + + public function testCompanyTaxAllOff() + { + + $invoice = $this->invoiceStub('92582'); + $client = $invoice->client; + $client->is_tax_exempt = false; + $client->save(); + + $company = $invoice->company; + $company->tax_all_products = false; + $tax_data = $company->tax_data; + + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all = false; + + $company->tax_data = $tax_data; + $company->save(); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(0, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(100, $invoice->amount); + + } + + + public function testThresholdLevelsAreMet() + { + + $invoice = $this->invoiceStub('92582'); + $client = $invoice->client; + $client->is_tax_exempt = true; + $client->save(); + + + $company = $invoice->company; + $tax_data = $company->tax_data; + + $tax_data->regions->US->has_sales_above_threshold = false; + $tax_data->regions->US->tax_all = true; + + $company->tax_data = $tax_data; + $company->save(); + + $invoice = $invoice->calc()->getInvoice()->service()->markSent()->save(); + + $this->assertEquals(0, $invoice->line_items[0]->tax_rate1); + $this->assertEquals(100, $invoice->amount); + + + } + public function testHasValidVatMakesNoDifferenceToTaxCalc() { @@ -158,10 +259,9 @@ class UsTaxTest extends TestCase $this->assertEquals(8.75, $invoice->line_items[0]->tax_rate1); $this->assertEquals(108.75, $invoice->amount); - - } + public function testTaxExemption() { $invoice = $this->invoiceStub('92582'); From c40cc829dd5a337b6a0268a186321984df6e24ef Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sat, 1 Apr 2023 09:56:26 +1100 Subject: [PATCH 27/38] Remove tax_all_products --- .../2023_03_24_054758_add_client_is_exempt_from_taxes.php | 1 + 1 file changed, 1 insertion(+) diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index b298eeb46708..5f03420d594c 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -21,6 +21,7 @@ return new class extends Migration Schema::table('companies', function (Illuminate\Database\Schema\Blueprint $table) { $table->mediumText('tax_data')->nullable()->change(); + $table->dropColumn('tax_all_products'); }); } From ed745bf30c2f127ac8c2a163d208108aa2f58545 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 2 Apr 2023 15:57:27 +1000 Subject: [PATCH 28/38] Fixes for account creation --- app/Http/Controllers/AccountController.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 8c004691dd9f..2c6cc2ef8b3d 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -11,16 +11,17 @@ namespace App\Http\Controllers; -use App\Http\Requests\Account\CreateAccountRequest; -use App\Http\Requests\Account\UpdateAccountRequest; -use App\Jobs\Account\CreateAccount; use App\Models\Account; +use App\Libraries\MultiDB; +use App\Utils\TruthSource; use App\Models\CompanyUser; +use Illuminate\Http\Response; +use App\Jobs\Account\CreateAccount; use App\Transformers\AccountTransformer; use App\Transformers\CompanyUserTransformer; -use App\Utils\TruthSource; use Illuminate\Foundation\Bus\DispatchesJobs; -use Illuminate\Http\Response; +use App\Http\Requests\Account\CreateAccountRequest; +use App\Http\Requests\Account\UpdateAccountRequest; class AccountController extends BaseController { @@ -146,8 +147,10 @@ class AccountController extends BaseController if (! ($account instanceof Account)) { return $account; } + + MultiDB::findAndSetDbByAccountKey($account->key); - $cu = CompanyUser::where('user_id', auth()->user()->id); + $cu = CompanyUser::where('user_id', $account->users()->first()->id); $company_user = $cu->first(); From 539076b3749d3c85eb9e572df23cdbd3aecb26be Mon Sep 17 00:00:00 2001 From: David Bomba Date: Sun, 2 Apr 2023 21:42:14 +1000 Subject: [PATCH 29/38] Minor fixes for hosted ninja --- app/Http/Controllers/HostedMigrationController.php | 2 ++ app/Http/Controllers/PreviewController.php | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/app/Http/Controllers/HostedMigrationController.php b/app/Http/Controllers/HostedMigrationController.php index 479b8d0d137a..386722ca6e1f 100644 --- a/app/Http/Controllers/HostedMigrationController.php +++ b/app/Http/Controllers/HostedMigrationController.php @@ -38,6 +38,8 @@ class HostedMigrationController extends Controller $account->hosted_company_count = 10; $account->save(); + MultiDB::findAndSetDbByAccountKey($account->key); + $company = $account->companies->first(); $company_token = CompanyToken::where('user_id', auth()->user()->id) diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php index eead3581a0e3..551b62c7c36e 100644 --- a/app/Http/Controllers/PreviewController.php +++ b/app/Http/Controllers/PreviewController.php @@ -145,6 +145,10 @@ class PreviewController extends BaseController ->design($design) ->build(); + + nlog($maker->getCompiledHTML()); + + if (request()->query('html') == 'true') { return $maker->getCompiledHTML(); } @@ -376,6 +380,8 @@ class PreviewController extends BaseController ->design($design) ->build(); + nlog($maker->getCompiledHTML()); + if (request()->query('html') == 'true') { return $maker->getCompiledHTML(); } From 6c0c0d9372b4359dfcf3303b00139d9a02b4f5e1 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 07:30:56 +1000 Subject: [PATCH 30/38] Set null for company tax_data --- ...3_03_24_054758_add_client_is_exempt_from_taxes.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index 5f03420d594c..ee59526750af 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -1,8 +1,9 @@ dropColumn('tax_all_products'); }); + Company::query() + ->cursor() + ->each(function ($company) { + $company->tax_data = null; + $company->save(); + }); } /** From bd7722f6f039c9d799367fd7eb161d44fdcca04a Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 07:48:59 +1000 Subject: [PATCH 31/38] Refactor for taxes --- app/DataMapper/Tax/BaseRule.php | 2 +- app/DataMapper/Tax/DE/Rule.php | 8 +++- app/DataMapper/Tax/RuleInterface.php | 2 +- app/DataMapper/Tax/US/Rule.php | 38 ++++++++++--------- app/Helpers/Invoice/InvoiceItemSum.php | 8 +--- ...054758_add_client_is_exempt_from_taxes.php | 9 +++++ tests/Unit/Tax/UsTaxTest.php | 1 - 7 files changed, 40 insertions(+), 28 deletions(-) diff --git a/app/DataMapper/Tax/BaseRule.php b/app/DataMapper/Tax/BaseRule.php index 26d01eb57f7d..39bf2f4833d0 100644 --- a/app/DataMapper/Tax/BaseRule.php +++ b/app/DataMapper/Tax/BaseRule.php @@ -99,7 +99,7 @@ class BaseRule implements RuleInterface return $this; } - public function tax(): self + public function tax($product_tax_type): self { return $this; } diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index 50d2c0d48a57..d45d0d74293b 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -81,10 +81,14 @@ class Rule extends BaseRule implements RuleInterface } //need to add logic here to capture if - public function tax(): self + public function tax($type): self { - if($this->client->is_tax_exempt) + + if ($this->client->is_tax_exempt) return $this->taxExempt(); + + if ($type) + return $this->taxByType($type); $this->tax_rate1 = $this->vat_rate; $this->tax_name1 = "MwSt."; diff --git a/app/DataMapper/Tax/RuleInterface.php b/app/DataMapper/Tax/RuleInterface.php index 3ed6f8d06e4f..c210f2809c6a 100644 --- a/app/DataMapper/Tax/RuleInterface.php +++ b/app/DataMapper/Tax/RuleInterface.php @@ -18,7 +18,7 @@ interface RuleInterface { public function init(); - public function tax(); + public function tax(mixed $type); public function taxByType($type); diff --git a/app/DataMapper/Tax/US/Rule.php b/app/DataMapper/Tax/US/Rule.php index 5be5aab6ddda..8dabf504dea5 100644 --- a/app/DataMapper/Tax/US/Rule.php +++ b/app/DataMapper/Tax/US/Rule.php @@ -55,13 +55,22 @@ class Rule implements RuleInterface return $this; } - public function tax(): self + public function tax($type): self { - if($this->client->is_tax_exempt) + + if ($this->client->is_tax_exempt) { return $this->taxExempt(); + } + else if($this->client->company->tax_data->regions->US->tax_all){ - $this->tax_rate1 = $this->tax_data->taxSales * 100; - $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; + $this->tax_rate1 = $this->tax_data->taxSales * 100; + $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; + + return $this; + } + + if($type) + return $this->taxByType($type); return $this; @@ -72,11 +81,6 @@ class Rule implements RuleInterface if(!$product_tax_type) return $this; - - if ($this->client->is_tax_exempt) { - return $this->taxExempt(); - } - match($product_tax_type){ Product::PRODUCT_TYPE_EXEMPT => $this->taxExempt(), Product::PRODUCT_TYPE_DIGITAL => $this->taxDigital(), @@ -101,7 +105,7 @@ class Rule implements RuleInterface public function taxDigital(): self { - $this->tax(); + $this->default(); return $this; } @@ -109,7 +113,7 @@ class Rule implements RuleInterface public function taxService(): self { if($this->tax_data->txbService == 'Y') - $this->tax(); + $this->default(); return $this; } @@ -117,30 +121,30 @@ class Rule implements RuleInterface public function taxShipping(): self { if($this->tax_data->txbFreight == 'Y') - $this->tax(); + $this->default(); return $this; } public function taxPhysical(): self { - $this->tax(); + $this->default(); return $this; } public function default(): self { - - $this->tax_name1 = 'Tax Exempt'; - $this->tax_rate1 = 0; + + $this->tax_rate1 = $this->tax_data->taxSales * 100; + $this->tax_name1 = "{$this->tax_data->geoState} Sales Tax"; return $this; } public function taxReduced(): self { - $this->tax(); + $this->default(); return $this; } diff --git a/app/Helpers/Invoice/InvoiceItemSum.php b/app/Helpers/Invoice/InvoiceItemSum.php index 732f343ee8b9..6e9cce110e71 100644 --- a/app/Helpers/Invoice/InvoiceItemSum.php +++ b/app/Helpers/Invoice/InvoiceItemSum.php @@ -199,12 +199,8 @@ class InvoiceItemSum */ private function calcTaxesAutomatically(): self { - 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); - } - + $this->rule->tax($this->item->tax_id ?? null); + $this->item->tax_name1 = $this->rule->tax_name1; $this->item->tax_rate1 = $this->rule->tax_rate1; diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index ee59526750af..0f340053f1b4 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -1,5 +1,6 @@ tax_data = null; $company->save(); }); + + Client::query() + ->cursor() + ->each(function ($client) { + $client->tax_data = null; + $client->save(); + }); + } /** diff --git a/tests/Unit/Tax/UsTaxTest.php b/tests/Unit/Tax/UsTaxTest.php index e5791ef9fdab..82f8dc9777ec 100644 --- a/tests/Unit/Tax/UsTaxTest.php +++ b/tests/Unit/Tax/UsTaxTest.php @@ -204,7 +204,6 @@ class UsTaxTest extends TestCase $client->save(); $company = $invoice->company; - $company->tax_all_products = false; $tax_data = $company->tax_data; $tax_data->regions->US->has_sales_above_threshold = true; From 641502ff594785c764c8268b76fc7bfdde7e2cde Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 08:02:38 +1000 Subject: [PATCH 32/38] Fixes for tax all rule --- app/DataMapper/Tax/DE/Rule.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/app/DataMapper/Tax/DE/Rule.php b/app/DataMapper/Tax/DE/Rule.php index d45d0d74293b..a90cbed240db 100644 --- a/app/DataMapper/Tax/DE/Rule.php +++ b/app/DataMapper/Tax/DE/Rule.php @@ -84,14 +84,21 @@ class Rule extends BaseRule implements RuleInterface public function tax($type): self { - if ($this->client->is_tax_exempt) + + if ($this->client->is_tax_exempt) { return $this->taxExempt(); + } elseif ($this->client->company->tax_data->regions->EU->tax_all) { + + $this->tax_rate1 = $this->vat_rate; + $this->tax_name1 = "MwSt."; + + + return $this; + } if ($type) return $this->taxByType($type); - $this->tax_rate1 = $this->vat_rate; - $this->tax_name1 = "MwSt."; return $this; From 96d65fe04127af54d5b177664124463172c7ba0e Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 08:03:56 +1000 Subject: [PATCH 33/38] Fixes for tests --- tests/Unit/Tax/EuTaxTest.php | 3 --- tests/Unit/Tax/SumTaxTest.php | 2 -- tests/Unit/Tax/UsTaxTest.php | 2 -- 3 files changed, 7 deletions(-) diff --git a/tests/Unit/Tax/EuTaxTest.php b/tests/Unit/Tax/EuTaxTest.php index 52ac72ac8c8e..f6eae304295b 100644 --- a/tests/Unit/Tax/EuTaxTest.php +++ b/tests/Unit/Tax/EuTaxTest.php @@ -60,7 +60,6 @@ class EuTaxTest extends TestCase 'settings' => $settings, 'tax_data' => $tax_data, 'calculate_taxes' => true, - 'tax_all_products' => true, ]); $client = Client::factory()->create([ @@ -124,7 +123,6 @@ class EuTaxTest extends TestCase 'settings' => $settings, 'tax_data' => $tax_data, 'calculate_taxes' => true, - 'tax_all_products' => true, ]); $client = Client::factory()->create([ @@ -189,7 +187,6 @@ class EuTaxTest extends TestCase 'settings' => $settings, 'tax_data' => $tax_data, 'calculate_taxes' => true, - 'tax_all_products' => true, ]); $client = Client::factory()->create([ diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index 471956e39bce..dc04b69e7ae2 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -91,7 +91,6 @@ class SumTaxTest extends TestCase public function testCalcInvoiceNoTax() { $this->company->calculate_taxes = false; - $this->company->tax_all_products = true; $this->company->save(); $client = Client::factory()->create([ @@ -134,7 +133,6 @@ class SumTaxTest extends TestCase { $this->company->calculate_taxes = true; - $this->company->tax_all_products = true; $this->company->save(); $client = Client::factory()->create([ diff --git a/tests/Unit/Tax/UsTaxTest.php b/tests/Unit/Tax/UsTaxTest.php index 82f8dc9777ec..e2299bc65da3 100644 --- a/tests/Unit/Tax/UsTaxTest.php +++ b/tests/Unit/Tax/UsTaxTest.php @@ -98,7 +98,6 @@ class UsTaxTest extends TestCase 'settings' => $settings, 'tax_data' => $tax_data, 'calculate_taxes' => true, - 'tax_all_products' => true, ]); $client = Client::factory()->create([ @@ -179,7 +178,6 @@ class UsTaxTest extends TestCase $client->save(); $company = $invoice->company; - $company->tax_all_products = false; $tax_data = $company->tax_data; $tax_data->regions->US->has_sales_above_threshold = true; From 7a96a4a208ff11266737982181d696b9c16eecb6 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 08:22:07 +1000 Subject: [PATCH 34/38] Fixes for tests --- tests/Unit/Tax/SumTaxTest.php | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/Unit/Tax/SumTaxTest.php b/tests/Unit/Tax/SumTaxTest.php index dc04b69e7ae2..5a67d72eff6f 100644 --- a/tests/Unit/Tax/SumTaxTest.php +++ b/tests/Unit/Tax/SumTaxTest.php @@ -18,8 +18,9 @@ use App\Models\Product; use Tests\MockAccountData; use App\DataMapper\InvoiceItem; use App\DataMapper\Tax\TaxData; -use App\DataMapper\Tax\ZipTax\Response; use App\Factory\InvoiceFactory; +use App\DataMapper\Tax\TaxModel; +use App\DataMapper\Tax\ZipTax\Response; use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Foundation\Testing\DatabaseTransactions; @@ -90,7 +91,16 @@ class SumTaxTest extends TestCase /** Proves that we do not charge taxes automatically */ public function testCalcInvoiceNoTax() { + + + $tax_data = new TaxModel(); + $tax_data->seller_region = 'US'; + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all = true; + $this->company->calculate_taxes = false; + $this->company->tax_data = $tax_data; $this->company->save(); $client = Client::factory()->create([ @@ -132,7 +142,16 @@ class SumTaxTest extends TestCase public function testCalcInvoiceTax() { + + + $tax_data = new TaxModel(); + $tax_data->seller_region = 'US'; + $tax_data->seller_subregion = 'CA'; + $tax_data->regions->US->has_sales_above_threshold = true; + $tax_data->regions->US->tax_all = true; + $this->company->calculate_taxes = true; + $this->company->tax_data = $tax_data; $this->company->save(); $client = Client::factory()->create([ From 4bbd4c93934a0ab71726cf33d6f1648ca69d386f Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 17:44:55 +1000 Subject: [PATCH 35/38] Remove logging --- app/Http/Controllers/PreviewController.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/Http/Controllers/PreviewController.php b/app/Http/Controllers/PreviewController.php index 551b62c7c36e..7e2ca56994a6 100644 --- a/app/Http/Controllers/PreviewController.php +++ b/app/Http/Controllers/PreviewController.php @@ -144,10 +144,6 @@ class PreviewController extends BaseController $maker ->design($design) ->build(); - - - nlog($maker->getCompiledHTML()); - if (request()->query('html') == 'true') { return $maker->getCompiledHTML(); @@ -380,8 +376,6 @@ class PreviewController extends BaseController ->design($design) ->build(); - nlog($maker->getCompiledHTML()); - if (request()->query('html') == 'true') { return $maker->getCompiledHTML(); } From 8ed37fb2d7cfddc265bc46e801f014d84ebabb52 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Mon, 3 Apr 2023 23:40:57 +1000 Subject: [PATCH 36/38] Fixes for adjust product inventory --- app/DataMapper/Tax/TaxModel.php | 4 ++-- app/Jobs/Inventory/AdjustProductInventory.php | 8 ++++---- app/Models/Product.php | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/DataMapper/Tax/TaxModel.php b/app/DataMapper/Tax/TaxModel.php index f127759d69cc..89872290cfd3 100644 --- a/app/DataMapper/Tax/TaxModel.php +++ b/app/DataMapper/Tax/TaxModel.php @@ -13,7 +13,7 @@ namespace App\DataMapper\Tax; class TaxModel { - public string $seller_region = 'US'; + // public string $seller_region = 'US'; public string $seller_subregion = 'CA'; @@ -45,7 +45,7 @@ class TaxModel private function usRegion(): self { $this->regions->US->has_sales_above_threshold = false; - $this->regions->US->tax_all = false; + $this->regions->US->tax_all_subregions = false; $this->usSubRegions(); return $this; diff --git a/app/Jobs/Inventory/AdjustProductInventory.php b/app/Jobs/Inventory/AdjustProductInventory.php index fddf8cabdc7c..e46ac69398f4 100644 --- a/app/Jobs/Inventory/AdjustProductInventory.php +++ b/app/Jobs/Inventory/AdjustProductInventory.php @@ -115,9 +115,9 @@ class AdjustProductInventory implements ShouldQueue $p->in_stock_quantity -= $i->quantity; $p->saveQuietly(); - nlog($p->stock_notification_threshold); - nlog($p->in_stock_quantity); - nlog($p->stock_notification_threshold); + nlog("threshold ".$p->stock_notification_threshold); + nlog("stock q".$p->in_stock_quantity); + nlog("p stock not".$p->stock_notification_threshold); if ($this->company->stock_notification && $p->stock_notification && $p->stock_notification_threshold && $p->in_stock_quantity <= $p->stock_notification_threshold) { $this->notifyStockLevels($p, 'product'); @@ -131,7 +131,7 @@ class AdjustProductInventory implements ShouldQueue private function existingInventoryAdjustment() { - collect($this->invoice->line_items)->filter(function ($item) { + collect($this->old_invoice)->filter(function ($item) { return $item->type_id == '1'; })->each(function ($i) { $p = Product::where('product_key', $i->product_key)->where('company_id', $this->company->id)->first(); diff --git a/app/Models/Product.php b/app/Models/Product.php index b04767ae6146..e360fd145ba7 100644 --- a/app/Models/Product.php +++ b/app/Models/Product.php @@ -114,7 +114,6 @@ class Product extends BaseModel use SoftDeletes; use Filterable; - public const PRODUCT_TYPE_PHYSICAL = 1; public const PRODUCT_TYPE_SERVICE = 2; public const PRODUCT_TYPE_DIGITAL = 3; From fbd47c1e408850e4f016cf1843bc347d963274b9 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 4 Apr 2023 07:15:19 +1000 Subject: [PATCH 37/38] Fixes for amount validation --- .../ClientPortal/PrePayments/StorePrePaymentRequest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Http/Requests/ClientPortal/PrePayments/StorePrePaymentRequest.php b/app/Http/Requests/ClientPortal/PrePayments/StorePrePaymentRequest.php index baa5e2a903f5..a1a4770cd6c9 100644 --- a/app/Http/Requests/ClientPortal/PrePayments/StorePrePaymentRequest.php +++ b/app/Http/Requests/ClientPortal/PrePayments/StorePrePaymentRequest.php @@ -26,7 +26,7 @@ class StorePrePaymentRequest extends FormRequest { return [ 'notes' => 'required|bail|', - 'amount' => 'required|bail|gte:minimum_amount', + 'amount' => 'required|bail|gte:minimum_amount|numeric', 'minimum_amount' => '', ]; } From e14656129f771a0edf74af999d7f4a0672eeb781 Mon Sep 17 00:00:00 2001 From: David Bomba Date: Tue, 4 Apr 2023 07:36:58 +1000 Subject: [PATCH 38/38] Set default tax id to 1 for all products --- app/DataMapper/Tax/TaxModel.php | 2 +- app/DataMapper/Tax/tax_model.yaml | 4 ++-- app/Factory/ProductFactory.php | 3 ++- ..._03_24_054758_add_client_is_exempt_from_taxes.php | 12 ++++++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/DataMapper/Tax/TaxModel.php b/app/DataMapper/Tax/TaxModel.php index 89872290cfd3..1d6be0261666 100644 --- a/app/DataMapper/Tax/TaxModel.php +++ b/app/DataMapper/Tax/TaxModel.php @@ -55,7 +55,7 @@ class TaxModel { $this->regions->EU->has_sales_above_threshold = false; - $this->regions->EU->tax_all = false; + $this->regions->EU->tax_all_subregions = false; $this->regions->EU->vat_threshold = 10000; $this->euSubRegions(); diff --git a/app/DataMapper/Tax/tax_model.yaml b/app/DataMapper/Tax/tax_model.yaml index 1288ad6320ff..3a1dd31f91d0 100644 --- a/app/DataMapper/Tax/tax_model.yaml +++ b/app/DataMapper/Tax/tax_model.yaml @@ -1,7 +1,7 @@ region: US: - tax_all: false - seller_region: CA + tax_all_subregions: false + seller_subregion: CA has_sales_above_threshold: false subregions: AL: diff --git a/app/Factory/ProductFactory.php b/app/Factory/ProductFactory.php index fbdd90d13264..08e5c5a2b9f6 100644 --- a/app/Factory/ProductFactory.php +++ b/app/Factory/ProductFactory.php @@ -35,7 +35,8 @@ class ProductFactory $product->custom_value3 = ''; $product->custom_value4 = ''; $product->is_deleted = 0; - + $product->tax_id = 1; + return $product; } } diff --git a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php index 0f340053f1b4..9de246de581d 100644 --- a/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php +++ b/database/migrations/2023_03_24_054758_add_client_is_exempt_from_taxes.php @@ -2,6 +2,7 @@ use App\Models\Client; use App\Models\Company; +use App\Models\Product; use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; @@ -26,6 +27,10 @@ return new class extends Migration $table->dropColumn('tax_all_products'); }); + Schema::table('products', function (Blueprint $table) { + $table->unsignedInteger('tax_id')->nullable(); // the product tax constant + }); + Company::query() ->cursor() ->each(function ($company) { @@ -40,6 +45,13 @@ return new class extends Migration $client->save(); }); + Product::query() + ->cursor() + ->each(function ($product) { + $product->tax_id = 1; + $product->save(); + }); + } /**