Skip to content

Commit

Permalink
Merge pull request #7 from bpolaszek/master
Browse files Browse the repository at this point in the history
New feature: fetch rate from an older date. Thanks @bpolaszek!
  • Loading branch information
dannyvankooten authored Feb 24, 2017
2 parents 80fef00 + 9bc7d24 commit 4646147
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 36 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ This library exposes 3 main classes to interact with, `Rates`, `Countries` and `
$rates = new DvK\Vat\Rates\Rates();
$rates->country('NL'); // 21
$rates->country('NL', 'standard'); // 21
$rates->country('NL', 'standard', new \Datetime('2010-01-01')); // 19
$rates->country('NL', 'reduced'); // 6
$rates->all(); // array in country code => rates format
```
Expand Down
4 changes: 2 additions & 2 deletions src/Rates/Caches/NullCache.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public function get($key, $default = null)
/**
* @param string $key
* @param mixed $value
* @param null|int|DateInterval $ttl
* @param null|int|\DateInterval $ttl
*
* @return bool
*/
Expand Down Expand Up @@ -58,7 +58,7 @@ public function getMultiple($keys, $default = null)

/**
* @param iterable $values
* @param null|int|DateInterval $ttl
* @param null|int|\DateInterval $ttl
*
* @return bool
*/
Expand Down
11 changes: 3 additions & 8 deletions src/Rates/Clients/JsonVat.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,9 @@ public function fetch() {
if( empty( $response_body ) ) {
throw new ClientException( "Error fetching rates from {$url}.");
}
$data = json_decode($response_body);

// build map with country codes => rates
$map = array();
foreach ($data->rates as $rate) {
$map[$rate->country_code] = $rate->periods[0]->rates;
}

return $map;
$data = json_decode($response_body, true);
$output = array_combine(array_column($data['rates'], 'country_code'), $data['rates']);
return $output;
}
}
27 changes: 22 additions & 5 deletions src/Rates/Interfaces/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,30 @@ interface Client {
* This methods should return an associative array in the following format:
*
* [
* 'DE' => [
* 'standard' => 19,
* 'reduced' => 7.0,
* ],
* ...
* 'NL' => [
* 'name' => 'Netherlands',
* 'code' => 'NL',
* 'country_code' => 'NL',
* 'periods' => [
* [
* 'effective_from' => '2012-10-01',
* 'rates' => [
* 'reduced' => 6.0,
* 'standard' => 21.0,
* ],
* ],
* [
* 'effective_from' => '0000-01-01',
* 'rates' => [
* 'reduced' => 5.0,
* 'standard' => 19.0,
* ],
* ],
* ],
* ]
* ]
*
*
* @throws ClientException
*
* @return array
Expand Down
35 changes: 30 additions & 5 deletions src/Rates/Rates.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Rates
protected $map = array();

/**
* @var Cache
* @var CacheInterface
*/
protected $cache;

Expand All @@ -30,7 +30,7 @@ class Rates
* Rates constructor.
*
* @param Client $client (optional)
* @param Cache $cache (optional)
* @param CacheInterface $cache (optional)
*/
public function __construct( Client $client = null, CacheInterface $cache = null )
{
Expand Down Expand Up @@ -80,25 +80,50 @@ public function all()
/**
* @param string $country
* @param string $rate
* @param \DateTimeInterface $applicableDate - optionnal - the applicable date
*
* @return double
*
* @throws Exception
*/
public function country($country, $rate = 'standard')
public function country($country, $rate = 'standard', \DateTimeInterface $applicableDate = null)
{
$country = strtoupper($country);
$country = $this->getCountryCode($country);

if (null === $applicableDate) {
$applicableDate = new \DateTime('today midnight');
}

if (!isset($this->map[$country])) {
throw new Exception('Invalid country code.');
}

if (!isset($this->map[$country]->$rate)) {
$periods = $this->map[$country]['periods'];

// Sort by date desc
usort($periods, function ($period1, $period2) {
return new \DateTime($period1['effective_from']) > new \DateTime($period2['effective_from']) ? -1 : 1;
});

foreach ($periods AS $period) {
if (new \DateTime($period['effective_from']) > $applicableDate) {
continue;
}
else {
break;
}
}

if (empty($period)) {
throw new Exception('Unable to find a rate applicable at that date.');
}

if (!isset($period['rates'][$rate])) {
throw new Exception('Invalid rate.');
}

return $this->map[$country]->$rate;
return $period['rates'][$rate];
}

/**
Expand Down
138 changes: 122 additions & 16 deletions tests/RatesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,38 +45,102 @@ public function getCacheMock() {
* @covers Rates::country
*/
public function test_country() {
$data = array(
'NL' => (object) [
'standard' => 21,
'reduced' => 15
$data = [
'NL' => [
'name' => 'Netherlands',
'code' => 'NL',
'country_code' => 'NL',
'periods' =>
[
[
'effective_from' => '2020-01-01',
'rates' =>
[
'reduced' => 7.0,
'standard' => 22.0,
],
],
[
'effective_from' => '2012-10-01',
'rates' =>
[
'reduced' => 6.0,
'standard' => 21.0,
],
],
[
'effective_from' => '0000-01-01',
'rates' =>
[
'reduced' => 5.0,
'standard' => 19.0,
],
],
],
]
);
];
$mock = $this->getClientMock();
$mock
->method('fetch')
->will(self::returnValue( $data ));

$rates = new Rates($mock, null);

// Return correct VAT rates
self::assertEquals( $rates->country('NL'), 21 );
self::assertEquals( $rates->country('NL', 'reduced'), 6 );

// Return correct VAT rates on an older period
self::assertEquals($rates->country('NL', 'standard', new \DateTimeImmutable('2010-01-01')), 19);
self::assertEquals($rates->country('NL', 'reduced', new \DateTimeImmutable('2010-01-01')), 5);

// Return correct VAT rates on an future period
self::assertEquals($rates->country('NL', 'standard', new \DateTimeImmutable('2022-01-01')), 22);
self::assertEquals($rates->country('NL', 'reduced', new \DateTimeImmutable('2022-01-01')), 7);

// Exception when supplying country code for which we have no rate
self::expectException( 'Exception' );
$rates->country('US');

// Return correct VAT rates
self::assertEquals( $rates->country('NL'), 21 );
self::assertEquals( $rates->country('NL', 'reduced'), 15 );
}

/**
* @covers Rates::all()
*/
public function test_all() {
$data = array(
'NL' => (object) [
'standard' => 21,
'reduced' => 15
$data = [
'NL' => [
'name' => 'Netherlands',
'code' => 'NL',
'country_code' => 'NL',
'periods' =>
[
[
'effective_from' => '2020-01-01',
'rates' =>
[
'reduced' => 7.0,
'standard' => 22.0,
],
],
[
'effective_from' => '2012-10-01',
'rates' =>
[
'reduced' => 6.0,
'standard' => 21.0,
],
],
[
'effective_from' => '0000-01-01',
'rates' =>
[
'reduced' => 5.0,
'standard' => 19.0,
],
],
],
]
);
];
$mock = $this->getClientMock();
$mock
->method('fetch')
Expand All @@ -91,7 +155,40 @@ public function test_all() {
*/
public function test_ratesAreLoadedFromCache() {
$mock = $this->getCacheMock();
$data = array( 'NL' => (object) [ 'standard' => 21, 'reduced' => 15 ]);
$data = [
'NL' => [
'name' => 'Netherlands',
'code' => 'NL',
'country_code' => 'NL',
'periods' =>
[
[
'effective_from' => '2020-01-01',
'rates' =>
[
'reduced' => 7.0,
'standard' => 22.0,
],
],
[
'effective_from' => '2012-10-01',
'rates' =>
[
'reduced' => 6.0,
'standard' => 21.0,
],
],
[
'effective_from' => '0000-01-01',
'rates' =>
[
'reduced' => 5.0,
'standard' => 19.0,
],
],
],
]
];

$mock
->method('get')
Expand All @@ -103,8 +200,17 @@ public function test_ratesAreLoadedFromCache() {
self::assertNotEmpty($rates->all());
self::assertEquals($rates->all(), $data);

// Return correct VAT rates
self::assertEquals($rates->country('NL'), 21);
self::assertEquals($rates->country('NL', 'reduced'), 15);
self::assertEquals($rates->country('NL', 'reduced'), 6);

// Return correct VAT rates on an older period
self::assertEquals($rates->country('NL', 'standard', new \DateTimeImmutable('2010-01-01')), 19);
self::assertEquals($rates->country('NL', 'reduced', new \DateTimeImmutable('2010-01-01')), 5);

// Return correct VAT rates on an future period
self::assertEquals($rates->country('NL', 'standard', new \DateTimeImmutable('2022-01-01')), 22);
self::assertEquals($rates->country('NL', 'reduced', new \DateTimeImmutable('2022-01-01')), 7);
}

/**
Expand Down

0 comments on commit 4646147

Please sign in to comment.