Refactor for taxes

This commit is contained in:
David Bomba 2023-03-27 07:46:26 +11:00
parent 046a72326e
commit d52d2f1f37
9 changed files with 266 additions and 105 deletions

View File

@ -11,6 +11,9 @@
namespace App\DataMapper\Tax; namespace App\DataMapper\Tax;
use App\Models\Client;
use App\DataMapper\Tax\ZipTax\Response;
interface RuleInterface interface RuleInterface
{ {
public function tax(); public function tax();
@ -27,5 +30,11 @@ interface RuleInterface
public function taxPhysical(); public function taxPhysical();
public function taxReduced();
public function default(); public function default();
public function setClient(Client $client);
public function setTaxData(Response $tax_data);
} }

View File

@ -11,6 +11,7 @@
namespace App\DataMapper\Tax\de; namespace App\DataMapper\Tax\de;
use App\Models\Client;
use App\Models\Product; use App\Models\Product;
use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\RuleInterface;
use App\DataMapper\Tax\ZipTax\Response; use App\DataMapper\Tax\ZipTax\Response;
@ -100,10 +101,24 @@ class Rule implements RuleInterface
public string $tax_name3 = ''; public string $tax_name3 = '';
public float $tax_rate3 = 0; 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; $this->tax_data = $tax_data;
return $this;
} }
public function tax(): self public function tax(): self
@ -126,12 +141,21 @@ class Rule implements RuleInterface
Product::PRODUCT_TYPE_SERVICE => $this->taxService(), Product::PRODUCT_TYPE_SERVICE => $this->taxService(),
Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(), Product::PRODUCT_TYPE_SHIPPING => $this->taxShipping(),
Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(), Product::PRODUCT_TYPE_PHYSICAL => $this->taxPhysical(),
Product::PRODUCT_TYPE_REDUCED_TAX => $this->taxReduced(),
default => $this->default(), default => $this->default(),
}; };
return $this; return $this;
} }
public function taxReduced(): self
{
$this->tax_rate1 = $this->vat_reduced_rate;
$this->tax_name1 = 'VAT';
return $this;
}
public function taxExempt(): self public function taxExempt(): self
{ {
$this->tax_name1 = ''; $this->tax_name1 = '';

View File

@ -11,6 +11,7 @@
namespace App\DataMapper\Tax\us; namespace App\DataMapper\Tax\us;
use App\Models\Client;
use App\Models\Product; use App\Models\Product;
use App\DataMapper\Tax\RuleInterface; use App\DataMapper\Tax\RuleInterface;
use App\DataMapper\Tax\ZipTax\Response; use App\DataMapper\Tax\ZipTax\Response;
@ -80,10 +81,26 @@ class Rule implements RuleInterface
public string $tax_name3 = ''; public string $tax_name3 = '';
public float $tax_rate3 = 0; 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; $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 public function tax(): self
@ -158,4 +175,11 @@ class Rule implements RuleInterface
return $this; return $this;
} }
public function taxReduced(): self
{
$this->tax();
return $this;
}
} }

View File

@ -112,8 +112,8 @@ class InvoiceItemSum
$tax_data = new Response($this->invoice->tax_data); $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; $this->calc_tax = true;
return $this; return $this;

View File

@ -116,6 +116,7 @@ class Product extends BaseModel
public const PRODUCT_TYPE_DIGITAL = 3; public const PRODUCT_TYPE_DIGITAL = 3;
public const PRODUCT_TYPE_SHIPPING = 4; public const PRODUCT_TYPE_SHIPPING = 4;
public const PRODUCT_TAX_EXEMPT = 5; public const PRODUCT_TAX_EXEMPT = 5;
public const PRODUCT_TYPE_REDUCED_TAX = 6;
protected $fillable = [ protected $fillable = [
'custom_value1', 'custom_value1',

View File

@ -9,14 +9,14 @@
* @license https://www.elastic.co/licensing/elastic-license * @license https://www.elastic.co/licensing/elastic-license
*/ */
namespace App\Services\Tax; namespace App\Services\Tax\Providers;
use App\Models\Client; use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use Illuminate\Support\Str; use Illuminate\Support\Str;
use App\DataMapper\Tax\de\Rule; use App\DataMapper\Tax\de\Rule;
use App\Services\Tax\VatNumberCheck; use App\Services\Tax\VatNumberCheck;
class ProcessRule class EuTax
{ {
public Rule $rule; public Rule $rule;

View File

@ -0,0 +1,194 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com).
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2023. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://www.elastic.co/licensing/elastic-license
*/
namespace App\Services\Tax\Providers;
use App\Models\Client;
use App\Models\Company;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Http;
class TaxProvider
{
public array $eu_countries = [
"AT",
"BE",
"BG",
"HR",
"CY",
"CZ",
"DK",
"EE",
"FI",
"FR",
"DE",
"GR",
"HU",
"IE",
"IT",
"LV",
"LT",
"LU",
"MT",
"NL",
"PL",
"PT",
"RO",
"SK",
"SI",
"ES",
"SE"
];
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($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;
}
}

View File

@ -18,101 +18,10 @@ use App\Services\Tax\Providers\ZipTax;
class TaxService class TaxService
{ {
private string $provider = ZipTax::class;
private mixed $api_credentials;
public function __construct(protected Company $company, protected Client $client) 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;
}
} }

View File

@ -16,15 +16,15 @@ use App\Models\Client;
use App\Models\Company; use App\Models\Company;
use Tests\MockAccountData; use Tests\MockAccountData;
use App\DataMapper\Tax\de\Rule; use App\DataMapper\Tax\de\Rule;
use App\Services\Tax\ProcessRule; use App\Services\Tax\Providers\EuTax;
use App\DataMapper\CompanySettings; use App\DataMapper\CompanySettings;
use Illuminate\Routing\Middleware\ThrottleRequests; use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Foundation\Testing\DatabaseTransactions; 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 MockAccountData;
use DatabaseTransactions; use DatabaseTransactions;
@ -60,7 +60,7 @@ class ProcessRuleTest extends TestCase
'shipping_country_id' => 276, 'shipping_country_id' => 276,
]); ]);
$process = new ProcessRule($company, $client); $process = new EuTax($company, $client);
$process->run(); $process->run();
$this->assertEquals('de', $process->getVendorCountryCode()); $this->assertEquals('de', $process->getVendorCountryCode());
@ -96,7 +96,7 @@ class ProcessRuleTest extends TestCase
'shipping_country_id' => 56, 'shipping_country_id' => 56,
]); ]);
$process = new ProcessRule($company, $client); $process = new EuTax($company, $client);
$process->run(); $process->run();
$this->assertEquals('de', $process->getVendorCountryCode()); $this->assertEquals('de', $process->getVendorCountryCode());
@ -132,7 +132,7 @@ class ProcessRuleTest extends TestCase
'shipping_country_id' => 840, 'shipping_country_id' => 840,
]); ]);
$process = new ProcessRule($company, $client); $process = new EuTax($company, $client);
$process->run(); $process->run();
$this->assertEquals('de', $process->getVendorCountryCode()); $this->assertEquals('de', $process->getVendorCountryCode());