Fixes for taxes

This commit is contained in:
David Bomba 2023-05-17 14:07:48 +10:00
parent 36269e4e1b
commit 8908bc318c
7 changed files with 95 additions and 53 deletions

View File

@ -16,6 +16,7 @@ use App\Models\Invoice;
use App\Models\Product;
use App\DataProviders\USStates;
use App\DataMapper\Tax\ZipTax\Response;
use App\Services\Tax\Providers\TaxProvider;
class BaseRule implements RuleInterface
{
@ -103,9 +104,6 @@ class BaseRule implements RuleInterface
/** EU TAXES */
/** US TAXES */
/** US TAXES */
public string $tax_name1 = '';
public float $tax_rate1 = 0;
@ -129,75 +127,132 @@ class BaseRule implements RuleInterface
{
return $this;
}
/**
* Initializes the tax rule for the entity.
*
* @param mixed $invoice
* @return self
*/
public function setEntity(mixed $invoice): self
{
$this->invoice = $invoice;
$this->client = $invoice->client;
$this->configTaxData()
->resolveRegions();
$this->resolveRegions()
->configTaxData();
$this->tax_data = new Response($this->invoice->tax_data);
return $this;
}
/**
* Configigures the Tax Data for the entity
*
* @return self
*/
private function configTaxData(): self
{
/* If the client Country is not in the region_codes, we force the company country onto the client? @TODO */
/* We should only apply taxes for configured states */
if(!array_key_exists($this->client->country->iso_3166_2, $this->region_codes)) {
// $this->client->country_id = $this->invoice->company->settings->country_id;
// $this->client->saveQuietly();
nlog('Automatic tax calculations not supported for this country - defaulting to company country');
nlog("With new logic, we should never see this");
}
/** Harvest the client_region */
$this->client_region = $this->region_codes[$this->client->country->iso_3166_2];
/** If the tax data is already set and the invoice is marked as sent, do not adjust the rates */
if($this->invoice->tax_data && $this->invoice->status_id > 1)
return $this;
//Pass the client tax data into the invoice tax data object
$tax_data = is_object($this->invoice->client->tax_data) ? $this->invoice->client->tax_data : new Response([]);
/**
* Origin - Company Tax Data
* Destination - Client Tax Data
*
*/
$tax_data = new Response([]);
/** If no Origin / Destination has been set and the seller and client sub regions are not the same, force destination tax */
if(strlen($this->invoice->tax_data?->originDestination) == 0 && $this->client->company->tax_data->seller_subregion != $this->client_subregion) {
$tax_data->originDestination = "D";
$tax_data->geoState = $this->client_subregion;
if($this->seller_region == 'US'){
$company = $this->invoice->company;
/** If no company tax data has been configured, lets do that now. */
if(!$company->origin_tax_data && \DB::transactionLevel() == 0)
{
$tp = new TaxProvider($company);
$tp->updateCompanyTaxData();
$company->fresh();
if($this->invoice instanceof Invoice) {
$this->invoice->tax_data = $tax_data;
$this->invoice->saveQuietly();
}
/** If we are in a Origin based state, force the company tax here */
if($company->origin_tax_data->originDestination == 'O' && ($company->tax_data->seller_subregion == $this->client_subregion)) {
$tax_data = $company->origin_tax_data;
}
else{
/** Ensures the client tax data has been updated */
if(!$this->client->tax_data && \DB::transactionLevel() == 0) {
$tp = new TaxProvider($company, $this->client);
$tp->updateClientTaxData();
$this->client->fresh();
}
$tax_data = $this->client->tax_data;
}
}
/** Applies the tax data to the invoice */
if($this->invoice instanceof Invoice && \DB::transactionLevel() == 0) {
$this->invoice->tax_data = $tax_data;
$this->invoice->saveQuietly();
}
return $this;
}
// Refactor to support switching between shipping / billing country / region / subregion
/**
* Resolve Regions & Subregions
*
* @return self
*/
private function resolveRegions(): self
{
$this->client_region = $this->region_codes[$this->client->country->iso_3166_2];
match($this->client_region){
'US' => $this->client_subregion = strlen($this->invoice?->tax_data?->geoState) > 1 ? $this->invoice?->tax_data?->geoState : $this->getUSState(),
'US' => $this->client_subregion = strlen($this->invoice?->client?->tax_data?->geoState) > 1 ? $this->invoice->client->tax_data->geoState : $this->getUSState(),
'EU' => $this->client_subregion = $this->client->country->iso_3166_2,
'AU' => $this->client_subregion = 'AU',
default => $this->client_subregion = $this->client->country->iso_3166_2,
};
return $this;
}
private function getUSState(): string
{
try {
$states = USStates::$states;
if(isset($states[$this->client->state]))
return $this->client->state;
return USStates::getState(strlen($this->client->postal_code) > 1 ? $this->client->postal_code : $this->client->shipping_postal_code);
} catch (\Exception $e) {
return $this->client->company->country()->iso_3166_2 == 'US' ? $this->client->company->tax_data->seller_subregion : 'CA';
}

View File

@ -159,18 +159,8 @@ class Rule extends BaseRule implements RuleInterface
if($this->tax_data?->stateSalesTax == 0) {
if($this->tax_data->originDestination == "O"){
$tax_region = $this->client->company->tax_data->seller_subregion;
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->US->subregions->{$tax_region}->tax_rate;
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
} else {
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
$this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
if($this->client_region == 'US')
$this->tax_name1 = "{$this->client_subregion} ".$this->tax_name1;
}
$this->tax_rate1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_rate;
$this->tax_name1 = $this->invoice->client->company->tax_data->regions->{$this->client_region}->subregions->{$this->client_subregion}->tax_name;
return $this;
}
@ -178,7 +168,6 @@ class Rule extends BaseRule implements RuleInterface
$this->tax_rate1 = $this->tax_data->taxSales * 100;
$this->tax_name1 = "{$this->tax_data->geoState} Sales Tax";
return $this;
}

View File

@ -16,7 +16,7 @@ use Illuminate\Support\Facades\Http;
class USStates
{
protected static array $states = [
public static array $states = [
'AL' => 'Alabama',
'AK' => 'Alaska',
'AZ' => 'Arizona',

View File

@ -146,9 +146,6 @@ class InvoiceItemSum
if (in_array($this->client->company->country()->iso_3166_2, $this->tax_jurisdictions) && in_array($this->client->country->iso_3166_2, $this->tax_jurisdictions)) { //only calculate for supported tax jurisdictions
nlog($this->client->country->iso_3166_2);
nlog($this->client->company->country()->iso_3166_2);
$class = "App\DataMapper\Tax\\".$this->client->company->country()->iso_3166_2."\\Rule";
$this->rule = new $class();

View File

@ -359,6 +359,7 @@ class Company extends BaseModel
'deleted_at' => 'timestamp',
'client_registration_fields' => 'array',
'tax_data' => 'object',
'origin_tax_data' => 'object',
'e_invoice_certificate_passphrase' => EncryptedCast::class,
];

View File

@ -52,14 +52,14 @@ class TaxProvider
private mixed $api_credentials;
public function __construct(public Company $company, public Client $client)
public function __construct(public Company $company, public ?Client $client = null)
{
}
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
$this->configureProvider($this->provider, $this->company->country()->iso_3166_2); //hard coded for now to one provider, but we'll be able to swap these out later
$company_details = [
'address1' => $this->company->settings->address1,
@ -76,7 +76,7 @@ class TaxProvider
$tax_data = $tax_provider->run();
$this->company->tax_data = $tax_data;
$this->company->origin_tax_data = $tax_data;
$this->company->save();
@ -86,7 +86,7 @@ class TaxProvider
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
$this->configureProvider($this->provider, $this->client->country->iso_3166_2); //hard coded for now to one provider, but we'll be able to swap these out later
$billing_details =[
'address1' => $this->client->address1,
@ -112,9 +112,7 @@ class TaxProvider
$tax_provider->setApiCredentials($this->api_credentials);
$tax_data = $tax_provider->run();
nlog($tax_data);
$this->client->tax_data = $tax_data;
$this->client->save();
@ -123,10 +121,10 @@ class TaxProvider
}
private function configureProvider(?string $provider): self
private function configureProvider(?string $provider, string $country_code): self
{
match($this->client->country->iso_3166_2){
match($country_code){
'US' => $this->configureZipTax(),
"AT" => $this->configureEuTax(),
"BE" => $this->configureEuTax(),
@ -169,11 +167,11 @@ class TaxProvider
return $this;
}
private function noTaxRegionDefined(): self
private function noTaxRegionDefined()
{
throw new \Exception("No tax region defined for this country");
return $this;
// return $this;
}
private function configureZipTax(): self

View File

@ -13,9 +13,11 @@ return new class extends Migration
*/
public function up()
{
Schema::table('companies', function (Illuminate\Database\Schema\Blueprint $table) {
$table->text('e_invoice_certificate')->nullable();
$table->text('e_invoice_certificate_passphrase')->nullable();
$table->text('origin_tax_data')->nullable();
});
}