Skip to content

Commit

Permalink
Issue #2872190 by bojanz: Create tax rate resolvers
Browse files Browse the repository at this point in the history
  • Loading branch information
bojanz committed Apr 26, 2017
1 parent 1173b61 commit b8a7444
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 9 deletions.
10 changes: 10 additions & 0 deletions modules/tax/commerce_tax.services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,16 @@ services:
class: Drupal\commerce_tax\TaxTypeManager
parent: default_plugin_manager

commerce_tax.chain_tax_rate_resolver:
class: Drupal\commerce_tax\Resolver\ChainTaxRateResolver
tags:
- { name: service_collector, call: addResolver, tag: commerce_tax.tax_rate_resolver }

commerce_tax.default_tax_rate_resolver:
class: Drupal\commerce_tax\Resolver\DefaultTaxRateResolver
tags:
- { name: commerce_tax.tax_rate_resolver, priority: -100 }

commerce_tax.tax_order_processor:
class: Drupal\commerce_tax\TaxOrderProcessor
arguments: ['@entity_type.manager']
Expand Down
8 changes: 6 additions & 2 deletions modules/tax/src/Plugin/Commerce/TaxType/Custom.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Drupal\commerce_tax\Plugin\Commerce\TaxType;

use Drupal\commerce_price\RounderInterface;
use Drupal\commerce_tax\Resolver\ChainTaxRateResolverInterface;
use Drupal\commerce_tax\TaxZone;
use Drupal\Component\Uuid\UuidInterface;
use Drupal\Component\Utility\Html;
Expand Down Expand Up @@ -43,11 +44,13 @@ class Custom extends LocalTaxTypeBase {
* The event dispatcher.
* @param \Drupal\commerce_price\RounderInterface $rounder
* The rounder.
* @param \Drupal\commerce_tax\ChainTaxRateResolverInterface $chain_rate_resolver
* The chain tax rate resolver.
* @param \Drupal\Component\Uuid\UuidInterface $uuid_generator
* The UUID generator.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, RounderInterface $rounder, UuidInterface $uuid_generator) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $event_dispatcher, $rounder);
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, RounderInterface $rounder, ChainTaxRateResolverInterface $chain_rate_resolver, UuidInterface $uuid_generator) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $event_dispatcher, $rounder, $chain_rate_resolver);

$this->uuidGenerator = $uuid_generator;
}
Expand All @@ -63,6 +66,7 @@ public static function create(ContainerInterface $container, array $configuratio
$container->get('entity_type.manager'),
$container->get('event_dispatcher'),
$container->get('commerce_price.rounder'),
$container->get('commerce_tax.chain_tax_rate_resolver'),
$container->get('uuid')
);
}
Expand Down
20 changes: 16 additions & 4 deletions modules/tax/src/Plugin/Commerce/TaxType/LocalTaxTypeBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Drupal\commerce_price\RounderInterface;
use Drupal\commerce_store\Entity\StoreInterface;
use Drupal\commerce_tax\TaxZone;
use Drupal\commerce_tax\Resolver\ChainTaxRateResolverInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\profile\Entity\ProfileInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
Expand All @@ -26,6 +27,13 @@ abstract class LocalTaxTypeBase extends TaxTypeBase implements LocalTaxTypeInter
*/
protected $rounder;

/**
* The chain tax rate resolver.
*
* @var \Drupal\commerce_tax\ChainTaxRateResolverInterface
*/
protected $chainRateResolver;

/**
* Constructs a new LocalTaxTypeBase object.
*
Expand All @@ -41,11 +49,14 @@ abstract class LocalTaxTypeBase extends TaxTypeBase implements LocalTaxTypeInter
* The event dispatcher.
* @param \Drupal\commerce_price\RounderInterface $rounder
* The rounder.
* @param \Drupal\commerce_tax\ChainTaxRateResolverInterface $chain_rate_resolver
* The chain tax rate resolver.
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, RounderInterface $rounder) {
public function __construct(array $configuration, $plugin_id, $plugin_definition, EntityTypeManagerInterface $entity_type_manager, EventDispatcherInterface $event_dispatcher, RounderInterface $rounder, ChainTaxRateResolverInterface $chain_rate_resolver) {
parent::__construct($configuration, $plugin_id, $plugin_definition, $entity_type_manager, $event_dispatcher);

$this->rounder = $rounder;
$this->chainRateResolver = $chain_rate_resolver;
}

/**
Expand All @@ -58,7 +69,8 @@ public static function create(ContainerInterface $container, array $configuratio
$plugin_definition,
$container->get('entity_type.manager'),
$container->get('event_dispatcher'),
$container->get('commerce_price.rounder')
$container->get('commerce_price.rounder'),
$container->get('commerce_tax.chain_tax_rate_resolver')
);
}

Expand Down Expand Up @@ -93,8 +105,8 @@ public function apply(OrderInterface $order) {

$zones = $this->resolveZones($order_item, $customer_profile);
foreach ($zones as $zone) {
$rate = $this->resolveRate($zone, $order_item, $customer_profile);
if (!$rate) {
$rate = $this->chainRateResolver->resolve($zone, $order_item, $customer_profile);
if (!is_object($rate)) {
// No applicable rate found.
continue;
}
Expand Down
57 changes: 57 additions & 0 deletions modules/tax/src/Resolver/ChainTaxRateResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php

namespace Drupal\commerce_tax\Resolver;

use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_tax\TaxZone;
use Drupal\profile\Entity\ProfileInterface;

class ChainTaxRateResolver implements ChainTaxRateResolverInterface {

/**
* The resolvers.
*
* @var \Drupal\commerce_tax\Resolver\TaxRateResolverInterface[]
*/
protected $resolvers = [];

/**
* Constructs a new ChainTaxRateResolver object.
*
* @param \Drupal\commerce_tax\Resolver\TaxRateResolverInterface[] $resolvers
* The resolvers.
*/
public function __construct(array $resolvers = []) {
$this->resolvers = $resolvers;
}

/**
* {@inheritdoc}
*/
public function addResolver(TaxRateResolverInterface $resolver) {
$this->resolvers[] = $resolver;
}

/**
* {@inheritdoc}
*/
public function getResolvers() {
return $this->resolvers;
}

/**
* {@inheritdoc}
*/
public function resolve(TaxZone $zone, OrderItemInterface $order_item, ProfileInterface $customer_profile) {
$result = NULL;
foreach ($this->resolvers as $resolver) {
$result = $resolver->resolve($zone, $order_item, $customer_profile);
if ($result) {
break;
}
}

return $result;
}

}
29 changes: 29 additions & 0 deletions modules/tax/src/Resolver/ChainTaxRateResolverInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace Drupal\commerce_tax\Resolver;

/**
* Runs the added resolvers one by one until one of them returns the tax rate.
*
* Each resolver in the chain can be another chain, which is why this interface
* extends the tax rate resolver one.
*/
interface ChainTaxRateResolverInterface extends TaxRateResolverInterface {

/**
* Adds a resolver.
*
* @param \Drupal\commerce_tax\Resolver\TaxRateResolverInterface $resolver
* The resolver.
*/
public function addResolver(TaxRateResolverInterface $resolver);

/**
* Gets all added resolvers.
*
* @return \Drupal\commerce_tax\Resolver\TaxRateResolverInterface[]
* The resolvers.
*/
public function getResolvers();

}
30 changes: 30 additions & 0 deletions modules/tax/src/Resolver/DefaultTaxRateResolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

namespace Drupal\commerce_tax\Resolver;

use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_tax\TaxZone;
use Drupal\profile\Entity\ProfileInterface;

/**
* Returns the tax zone's default tax rate.
*/
class DefaultTaxRateResolver implements TaxRateResolverInterface {

/**
* {@inheritdoc}
*/
public function resolve(TaxZone $zone, OrderItemInterface $order_item, ProfileInterface $customer_profile) {
$rates = $zone->getRates();
// Take the default rate, or fallback to the first rate.
$resolved_rate = reset($rates);
foreach ($rates as $rate) {
if ($rate->isDefault()) {
$resolved_rate = $rate;
break;
}
}
return $resolved_rate;
}

}
33 changes: 33 additions & 0 deletions modules/tax/src/Resolver/TaxRateResolverInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Drupal\commerce_tax\Resolver;

use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_tax\TaxZone;
use Drupal\profile\Entity\ProfileInterface;

/**
* Defines the interface for tax rate resolvers.
*/
interface TaxRateResolverInterface {

// Stops resolving when there is no applicable tax rate (cause the
// provided order item is exempt from sales tax, for example).
const NO_APPLICABLE_TAX_RATE = 'no_applicable_tax_rate';

/**
* Resolves the tax rate for the given tax zone.
*
* @param \Drupal\commerce_tax\TaxZone $zone
* The tax zone.
* @param \Drupal\commerce_order\Entity\OrderItemInterface $order_item
* The order item.
* @param \Drupal\profile\Entity\ProfileInterface $customer_profile
* The customer profile. Contains the address and tax number.
*
* @return \Drupal\commerce_tax\TaxRate|string|null
* The tax rate, NO_APPLICABLE_TAX_RATE, or NULL.
*/
public function resolve(TaxZone $zone, OrderItemInterface $order_item, ProfileInterface $customer_profile);

}
4 changes: 2 additions & 2 deletions modules/tax/src/TaxRate.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function getAmounts() {
}

/**
* Gets the amount valid for the provided date.
* Gets the amount valid for the given date.
*
* @param \Drupal\Core\Datetime\DrupalDateTime $date
* The date.
Expand All @@ -106,7 +106,7 @@ public function getAmount(DrupalDateTime $date = NULL) {
// Default to the current date.
$date = $date ?: new DrupalDateTime();
// Amount start/end dates don't include the time, so discard the time
// portion of the provided date to make the matching precise.
// portion of the given date to make the matching precise.
$date->setTime(0, 0);
foreach ($this->amounts as $amount) {
$start_date = $amount->getStartDate();
Expand Down
2 changes: 1 addition & 1 deletion modules/tax/src/TaxZone.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public function getRates() {
}

/**
* Checks whether the provided address belongs to the zone.
* Checks whether the given address belongs to the zone.
*
* @param \CommerceGuys\Addressing\AddressInterface $address
* The address.
Expand Down

0 comments on commit b8a7444

Please sign in to comment.