diff --git a/README.md b/README.md index da50f7b..97a0358 100644 --- a/README.md +++ b/README.md @@ -167,6 +167,7 @@ The returned array of rates is unsorted. This method can be useful when you want EU countries are supported as well as some non-EU countries that use VAT. Some countries are not supported even though they also have VAT. Currently, that's the case for the following countries: - Switzerland (CH) +- United Kingdom (GB) - Norway (NO) - Turkey (TR) diff --git a/src/VatRates.php b/src/VatRates.php index dad54a2..aae7100 100644 --- a/src/VatRates.php +++ b/src/VatRates.php @@ -95,14 +95,6 @@ class VatRates 'Mayotte' => 0, ], ], - 'GB' => [ // United Kingdom - 'rate' => 0.20, - 'exceptions' => [ - // UK RAF Bases in Cyprus are taxed at Cyprus rate - 'Akrotiri' => 0.19, - 'Dhekelia' => 0.19, - ], - ], 'GR' => [ // Greece 'rate' => 0.24, 'exceptions' => [ @@ -200,6 +192,14 @@ class VatRates self::LOW => 0.025, ], ], + 'GB' => [ // United Kingdom + 'rate' => 0.20, + 'exceptions' => [ + // UK RAF Bases in Cyprus are taxed at Cyprus rate + 'Akrotiri' => 0.19, + 'Dhekelia' => 0.19, + ], + ], 'NO' => [ // Norway 'rate' => 0.25, ], @@ -311,18 +311,6 @@ class VatRates 'name' => 'Mayotte', ], ], - 'GB' => [ - // Akrotiri - [ - 'postalCode' => '/^BFPO57|BF12AT$/', - 'code' => 'CY', - ], - // Dhekelia - [ - 'postalCode' => '/^BFPO58|BF12AU$/', - 'code' => 'CY', - ], - ], 'GR' => [ [ 'postalCode' => '/^63086$/', @@ -358,6 +346,29 @@ class VatRates ], ]; + /** + * Optional postal code exceptions. + * + * Non-EU countries with their own VAT requirements and postal code exceptions, + * added with `addRateForCountry()` for the rate and the exceptions to be applied. + * + * @var array + */ + private $optionalPostalCodeExceptions = [ + 'GB' => [ + // Akrotiri + [ + 'postalCode' => '/^BFPO57|BF12AT$/', + 'code' => 'CY', + ], + // Dhekelia + [ + 'postalCode' => '/^BFPO58|BF12AU$/', + 'code' => 'CY', + ], + ], + ]; + /** @var DateTimeImmutable */ private $now; @@ -379,6 +390,9 @@ public function addRateForCountry(string $country): void throw new NoVatRulesForCountryException("No optional tax rules specified for {$country}"); } $this->taxRules[$country] = $this->optionalTaxRules[$country]; + if (isset($this->optionalPostalCodeExceptions[$country])) { + $this->postalCodeExceptions[$country] = $this->optionalPostalCodeExceptions[$country]; + } } diff --git a/tests/VatCalculatorTest.php b/tests/VatCalculatorTest.php index 3c9f523..4283c35 100644 --- a/tests/VatCalculatorTest.php +++ b/tests/VatCalculatorTest.php @@ -235,6 +235,35 @@ public function testAddNonEuRateShouldCollectValidateThrows(): void } + public function testAddGbRateShouldCollectWithPostalCodeException(): void + { + $this->assertFalse($this->vatCalculator->shouldCollectVat('GB')); + $this->assertFalse($this->vatCalculator->shouldCollectEuVat('GB')); + $result = $this->vatCalculator->calculate(24.00, 'GB', null, true); + $this->assertEquals(24.00, $result->getPrice()); + $this->assertEquals(0.00, $result->getTaxRate()); + $this->assertEquals(0.00, $result->getTaxValue()); + + $this->vatRates->addRateForCountry('GB'); + $this->assertTrue($this->vatCalculator->shouldCollectVat('GB')); + $this->assertFalse($this->vatCalculator->shouldCollectEuVat('GB')); + + // Valid UK post code + $postalCode = 'S1A 2AA'; + $result = $this->vatCalculator->calculate(24.00, 'GB', $postalCode, false); + //Expect standard rate for UK + $this->assertEquals(28.80, $result->getPrice()); + $this->assertEquals(0.20, $result->getTaxRate()); + $this->assertEquals(4.80, $result->getTaxValue()); + + $postalCode = 'BFPO58'; // Dhekelia + $result = $this->vatCalculator->calculate(24.00, 'GB', $postalCode, false); + $this->assertEquals(28.56, $result->getPrice()); + $this->assertEquals(0.19, $result->getTaxRate()); + $this->assertEquals(4.56, $result->getTaxValue()); + } + + public function testSetBusinessCountryCode(): void { $this->vatCalculator->setBusinessCountryCode('DE'); @@ -265,12 +294,6 @@ public function testChecksPostalCodeForVatExceptions(): void $this->assertEquals(0.19, $result->getTaxRate()); $this->assertEquals(4.56, $result->getTaxValue()); - $postalCode = 'BFPO58'; // Dhekelia - $result = $this->vatCalculator->calculate(24.00, 'GB', $postalCode, false); - $this->assertEquals(28.56, $result->getPrice()); - $this->assertEquals(0.19, $result->getTaxRate()); - $this->assertEquals(4.56, $result->getTaxValue()); - $postalCode = '9122'; // Madeira $result = $this->vatCalculator->calculate(24.00, 'PT', $postalCode, false); $this->assertEquals(29.28, $result->getPrice()); @@ -288,14 +311,6 @@ public function testPostalCodesWithoutExceptionsGetStandardRate(): void $this->assertEquals(29.04, $result->getPrice()); $this->assertEquals(0.21, $result->getTaxRate()); $this->assertEquals(5.04, $result->getTaxValue()); - - // Valid UK post code - $postalCode = 'S1A 2AA'; - $result = $this->vatCalculator->calculate(24.00, 'GB', $postalCode, false); - //Expect standard rate for UK - $this->assertEquals(28.80, $result->getPrice()); - $this->assertEquals(0.20, $result->getTaxRate()); - $this->assertEquals(4.80, $result->getTaxValue()); }