Skip to content
This repository has been archived by the owner on Oct 27, 2024. It is now read-only.

Commit

Permalink
Make the VatCalculator object immutable by returning VatPrice
Browse files Browse the repository at this point in the history
  • Loading branch information
spaze committed May 31, 2020
1 parent d63fcf6 commit 704f40f
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 379 deletions.
43 changes: 19 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,8 @@ use Spaze\VatCalculator\VatCalculator;

$vatRates = new VatRates();
$vatCalculator = new VatCalculator($vatRates);
$countryCode = $vatCalculator->getIpBasedCountry();
$vatCalculator->calculate( 24.00, $countryCode );
$vatCalculator->calculate( 24.00, $countryCode, $postalCode );
$vatCalculator->calculate( 71.00, 'DE', '41352', $isCompany = true );
$vatCalculator->getTaxRateForLocation( 'NL' );
$vatCalculator->calculate(71.00, 'DE' /* $countryCode */, '41352' /* $postalCode or null */, true /* Whether the customer you're calculating the VAT for is a company */);
$vatCalculator->getTaxRateForLocation('NL');
// Check validity of a VAT number
$vatCalculator->isValidVatNumber('NL123456789B01');
```
Expand All @@ -26,7 +23,6 @@ $vatCalculator->isValidVatNumber('NL123456789B01');
- [Standalone](#installation-standalone)
- [Usage](#usage)
- [Calculate the gross price](#calculate-the-gross-price)
- [Receive more information](#receive-more-information)
- [Validate EU VAT numbers](#validate-eu-vat-numbers)
- [Get EU VAT number details](#vat-number-details)
- [Get the IP based country of your user](#get-ip-based-country)
Expand Down Expand Up @@ -55,37 +51,36 @@ use Spaze\VatCalculator\VatCalculator;

$vatRates = new VatRates();
$vatCalculator = new VatCalculator($vatRates);
$vatCalculator->setBusinessCountryCode('DE');
$countryCode = $vatCalculator->getIpBasedCountry();
$grossPrice = $vatCalculator->calculate( 49.99, 'LU' );
$vatCalculator->setBusinessCountryCode('DE'); // Where your company is based in
$price = $vatCalculator->calculate(49.99, 'LU', null, false);
$price->getPrice();
$price->getNetPrice();
$price->getTaxValue();
$price->getTaxRate();
```

<a name="usage"></a>
## Usage
<a name="calculate-the-gross-price"></a>
### Calculate the gross price
To calculate the gross price use the `calculate` method with a net price and a country code as paremeters.
To calculate the gross price (price with VAT added) use the `calculate` method with a net price, a country code, a postal code (null if unknow) and whether you're calculating VAT for a customer that's a company as paremeters.

```php
$grossPrice = $vatCalculator->calculate( 24.00, 'DE' );
$grossPrice = $vatCalculator->calculate(24.00, 'DE', null, false);
```
The third parameter is the postal code of the customer.
The third parameter is the postal code of the customer, pass `null` if unknown.

As a fourth parameter, you can pass in a boolean indicating whether the customer is a company or a private person. If the customer is a company, which you should check by <a href="#validate-eu-vat-numbers">validating the VAT number</a>, the net price gets returned.

Fifth parameter defines which VAT rate to use if there are more defined for the particular country (`VatRates::HIGH`, `VatRates::LOW`, `VatRates::GENERAL` is the default when just one rate is defined).

Returns `VatPrice` object:
```php
$grossPrice = $vatCalculator->calculate( 24.00, 'DE', '12345', $isCompany = true );
```
<a name="receive-more-information"></a>
### Receive more information
After calculating the gross price you can extract more information from the VatCalculator.
$grossPrice->getPrice();
$grossPrice->getNetPrice();
$grossPrice->getTaxValue();
$grossPrice->getTaxRate();

```php
$grossPrice = $vatCalculator->calculate( 24.00, 'DE' ); // 28.56
$taxRate = $vatCalculator->getTaxRate(); // 0.19
$netPrice = $vatCalculator->getNetPrice(); // 24.00
$taxValue = $vatCalculator->getTaxValue(); // 4.56
```

<a name="validate-eu-vat-numbers"></a>
Expand All @@ -109,7 +104,7 @@ This service relies on a third party SOAP API provided by the EU. If, for whatev
```php
try {
$validVat = $vatCalculator->isValidVatNumber('NL 123456789 B01');
} catch( VatCheckUnavailableException $e ){
} catch (VatCheckUnavailableException $e) {
// Please handle me
}
```
Expand All @@ -136,7 +131,7 @@ try {
[requestId:VatDetails:private] => FOOBAR338
)
*/
} catch( VatCheckUnavailableException $e ){
} catch (VatCheckUnavailableException $e) {
// Please handle me
}
```
Expand Down
2 changes: 2 additions & 0 deletions UPGRADE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* Exceptions (`VatCheckUnavailableException`) are always thrown, `forwardSoapFaults` option has been removed (**BC BREAK**)
* Some countries have various VAT rates depending on location resulting in `getTaxRateForCountry()` removal, use `getTaxRateForLocation()` instead (**BC BREAK**)
* Rates have been moved to a separate class `VatRates`, you need to pass the class to `VatCalculator` constructor (**BC BREAK**)
* `calculate()` & `calculateNet()` methods return `VatPrice` object instead of the calculated price (**BR BREAK**)
* After running `calculate()` & `calculateNet()`, the `VatCalculator` object keeps its state, `getNetPrice()`, `getTaxRate()`, `getCountryCode()`, `setCountryCode()`, `getPostalCode()`, `setPostalCode()`, `isCompany()`, `setCompany()` removed (**BR BREAK**)
* Norway VAT rate removed, can be manually added back with `VatRates::addRateForCountry()`
* `getIPBasedCountry()` & `getClientIP()` methods have been removed, use some other package (or `CF-IPCountry` HTTP header if you're behind Cloudflare)
* Some methods have been properly *camelCased*: methods like `getClientIP()` -> `getClientIp()` and `shouldCollectVAT` -> `shouldCollectVat` and a few more
Expand Down
129 changes: 17 additions & 112 deletions src/VatCalculator.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,6 @@ class VatCalculator
/** @var VatRates */
private $vatRates;

/** @var float */
private $netPrice = 0.0;

/** @var string */
private $countryCode;

/** @var string */
private $postalCode;

/** @var float */
private $taxValue = 0;

/** @var float */
private $taxRate = 0;

/** @var bool */
private $company = false;

/** @var string */
private $businessCountryCode;

Expand All @@ -65,27 +47,17 @@ public function shouldCollectVat(string $countryCode): bool
* customer is a company or not.
*
* @param float $netPrice
* @param string|null $countryCode
* @param string $countryCode
* @param string|null $postalCode
* @param bool|null $company
* @param string|null $type
* @return float
* @param bool $company
* @param string $type
* @return VatPrice
*/
public function calculate(float $netPrice, ?string $countryCode = null, ?string $postalCode = null, ?bool $company = null, ?string $type = VatRates::GENERAL): float
public function calculate(float $netPrice, string $countryCode, ?string $postalCode, bool $company, string $type = VatRates::GENERAL): VatPrice
{
if ($countryCode) {
$this->setCountryCode($countryCode);
}
if ($postalCode) {
$this->setPostalCode($postalCode);
}
if (!is_null($company) && $company !== $this->isCompany()) {
$this->setCompany($company);
}
$this->netPrice = floatval($netPrice);
$this->taxRate = $this->getCountryCode() === null ? 0 : $this->getTaxRateForLocation($this->getCountryCode(), $this->getPostalCode(), $this->isCompany(), $type);
$this->taxValue = $this->taxRate * $this->netPrice;
return $this->netPrice + $this->taxValue;
$taxRate = $this->getTaxRateForLocation($countryCode, $postalCode, $company, $type);
$taxValue = $taxRate * $netPrice;
return new VatPrice($netPrice, $netPrice + $taxValue, $taxValue, $taxRate);
}


Expand All @@ -94,78 +66,17 @@ public function calculate(float $netPrice, ?string $countryCode = null, ?string
* customer is a company or not.
*
* @param float $gross
* @param string|null $countryCode
* @param string $countryCode
* @param string|null $postalCode
* @param bool|null $company
* @param string|null $type
* @return float
* @param bool $company
* @param string $type
* @return VatPrice
*/
public function calculateNet(float $gross, ?string $countryCode = null, ?string $postalCode = null, ?bool $company = null, ?string $type = VatRates::GENERAL): float
{
if ($countryCode) {
$this->setCountryCode($countryCode);
}
if ($postalCode) {
$this->setPostalCode($postalCode);
}
if (!is_null($company) && $company !== $this->isCompany()) {
$this->setCompany($company);
}

$value = floatval($gross);
$this->taxRate = $this->getCountryCode() === null ? 0 : $this->getTaxRateForLocation($this->getCountryCode(), $this->getPostalCode(), $this->isCompany(), $type);
$this->taxValue = $this->taxRate > 0 ? $value / (1 + $this->taxRate) * $this->taxRate : 0;
$this->netPrice = $value - $this->taxValue;

return $this->netPrice;
}


public function getNetPrice(): float
{
return $this->netPrice;
}


public function getCountryCode(): ?string
{
return $this->countryCode ? strtoupper($this->countryCode) : null;
}


public function setCountryCode(string $countryCode): void
{
$this->countryCode = $countryCode;
}


public function getPostalCode(): ?string
{
return $this->postalCode;
}


public function setPostalCode(string $postalCode): void
{
$this->postalCode = $postalCode;
}


public function getTaxRate(): float
{
return $this->taxRate;
}


public function isCompany(): bool
public function calculateNet(float $gross, string $countryCode, ?string $postalCode, bool $company, string $type = VatRates::GENERAL): VatPrice
{
return $this->company;
}


public function setCompany(bool $company): void
{
$this->company = $company;
$taxRate = $this->getTaxRateForLocation($countryCode, $postalCode, $company, $type);
$taxValue = $taxRate > 0 ? $gross / (1 + $taxRate) * $taxRate : 0;
return new VatPrice($gross - $taxValue, $gross, $taxValue, $taxRate);
}


Expand All @@ -192,7 +103,7 @@ public function setBusinessVatNumber(string $businessVatNumber): void
* @param string|null $type
* @return float
*/
public function getTaxRateForLocation(string $countryCode, ?string $postalCode = null, bool $company = false, ?string $type = null): float
public function getTaxRateForLocation(string $countryCode, ?string $postalCode, bool $company, ?string $type = null): float
{
if ($company && strtoupper($countryCode) !== $this->businessCountryCode) {
return 0;
Expand All @@ -202,12 +113,6 @@ public function getTaxRateForLocation(string $countryCode, ?string $postalCode =
}


public function getTaxValue(): float
{
return $this->taxValue;
}


/**
* @param string $vatNumber
* @return bool
Expand Down
56 changes: 56 additions & 0 deletions src/VatPrice.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php
declare(strict_types = 1);


namespace Spaze\VatCalculator;


class VatPrice
{

/** @var float */
private $netPrice;

/** @var float */
private $price;

/** @var float */
private $taxValue;

/** @var float */
private $taxRate;


public function __construct(float $netPrice, float $price, float $taxValue, float $taxRate)
{
$this->netPrice = $netPrice;
$this->price = $price;
$this->taxValue = $taxValue;
$this->taxRate = $taxRate;
}


public function getNetPrice(): float
{
return $this->netPrice;
}


public function getPrice(): float
{
return $this->price;
}


public function getTaxValue(): float
{
return $this->taxValue;
}


public function getTaxRate(): float
{
return $this->taxRate;
}

}
4 changes: 2 additions & 2 deletions src/VatRates.php
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ public function shouldCollectVat(string $countryCode): bool
* @param string|null $type
* @return float
*/
public function getTaxRateForLocation(string $countryCode, ?string $postalCode = null, ?string $type = self::GENERAL): float
public function getTaxRateForLocation(string $countryCode, ?string $postalCode, ?string $type = self::GENERAL): float
{
if (isset($this->postalCodeExceptions[$countryCode]) && $postalCode !== null) {
foreach ($this->postalCodeExceptions[$countryCode] as $postalCodeException) {
Expand All @@ -380,7 +380,7 @@ public function getTaxRateForLocation(string $countryCode, ?string $postalCode =
}
}

if ($type !== null) {
if ($type !== VatRates::GENERAL) {
return isset($this->taxRules[strtoupper($countryCode)]['rates'][$type]) ? $this->taxRules[strtoupper($countryCode)]['rates'][$type] : 0;
}

Expand Down
Loading

0 comments on commit 704f40f

Please sign in to comment.