Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add registration fields to stores for commerce tax zones. #846

Open
wants to merge 2 commits into
base: 8.x-2.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions modules/tax/commerce_tax.module
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ function commerce_tax_entity_base_field_info(EntityTypeInterface $entity_type) {
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);

$tax_plugin_manager = \Drupal::service('plugin.manager.commerce_tax_type');
foreach ($tax_plugin_manager->getDefinitions() as $definition) {
$plugin = $tax_plugin_manager->createInstance($definition['id']);
$fields += $plugin->storeFields();
}
return $fields;
}
}
Expand All @@ -58,6 +63,23 @@ function commerce_tax_form_commerce_store_form_alter(&$form, FormStateInterface
];
$form['prices_include_tax']['#group'] = 'tax_settings';
$form['tax_registrations']['#group'] = 'tax_settings';
$settings = [];
$settings['#group'] = 'tax_settings';

// @todo Fix when https://www.drupal.org/project/drupal/issues/1149078 has
// landed. Ideally these should only show up for stores that have selected
// CA in tax registrations. However, the States API is broken for select
// elements with #multiple => TRUE.
// $settings['#states'] = [
// 'visible' => [
// ':input[name^="tax_registrations"]' => ['value' => 'CA'],
// ]
// ];

$form['tax_ca_gst_number'] += $settings;
$form['tax_ca_pst_bc_number'] += $settings;
$form['tax_ca_pst_mb_number'] += $settings;
$form['tax_ca_qst_number'] += $settings;
}
}

Expand Down
64 changes: 61 additions & 3 deletions modules/tax/src/Plugin/Commerce/TaxType/CanadianSalesTax.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Drupal\commerce_order\Entity\OrderItemInterface;
use Drupal\commerce_store\Entity\StoreInterface;
use Drupal\commerce_tax\TaxZone;
use Drupal\Core\Field\BaseFieldDefinition;
use Drupal\Core\Form\FormStateInterface;
use Drupal\profile\Entity\ProfileInterface;

Expand Down Expand Up @@ -41,18 +42,24 @@ protected function matchesAddress(StoreInterface $store) {
protected function matchesRegistrations(StoreInterface $store) {
$store_registrations = $store->get('tax_registrations')->getValue();
$store_registrations = array_column($store_registrations, 'value');
return in_array('CA', $store_registrations);
if (in_array('CA', $store_registrations)) {
foreach ($this->getZones() as $zone) {
if ($zone->isRegistered($store)) {
return TRUE;
}
}
}
}

/**
* {@inheritdoc}
*/
protected function resolveZones(OrderItemInterface $order_item, ProfileInterface $customer_profile) {
protected function resolveZones(OrderItemInterface $order_item, ProfileInterface $customer_profile, StoreInterface $store) {
$customer_address = $customer_profile->get('address')->first();
if ($customer_address->getCountryCode() != 'CA') {
return [];
}
return parent::resolveZones($order_item, $customer_profile);
return parent::resolveZones($order_item, $customer_profile, $store);
}

/**
Expand Down Expand Up @@ -85,6 +92,7 @@ public function buildZones() {
'default' => TRUE,
],
],
'registration' => 'tax_ca_gst_number',
]);
$zones['bc'] = new TaxZone([
'id' => 'bc',
Expand All @@ -102,6 +110,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_pst_bc_number',
]);
$zones['mb'] = new TaxZone([
'id' => 'mb',
Expand All @@ -119,6 +128,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_pst_mb_number',
]);
$zones['nb'] = new TaxZone([
'id' => 'nb',
Expand All @@ -136,6 +146,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_gst_number',
]);
$zones['nl'] = new TaxZone([
'id' => 'nl',
Expand All @@ -153,6 +164,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_gst_number',
]);
$zones['ns'] = new TaxZone([
'id' => 'ns',
Expand All @@ -170,6 +182,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_gst_number',
]);
$zones['on'] = new TaxZone([
'id' => 'on',
Expand All @@ -187,6 +200,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_gst_number',
]);
$zones['pe'] = new TaxZone([
'id' => 'pe',
Expand All @@ -204,6 +218,7 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_gst_number',
]);
$zones['qc'] = new TaxZone([
'id' => 'qc',
Expand All @@ -221,9 +236,52 @@ public function buildZones() {
],
],
],
'registration' => 'tax_ca_qst_number',
]);

return $zones;
}

/**
* {@inheritdoc}
*/
public function storeFields() {
$fields = [];

$fields['tax_ca_gst_number'] = BaseFieldDefinition::create('string')
->setLabel(t('Canada GST/HST #'))
->setDisplayOptions('form', [
'type' => 'textfield',
'weight' => 5,
])
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);
$fields['tax_ca_pst_bc_number'] = BaseFieldDefinition::create('string')
->setLabel(t('British Columbia PST #'))
->setDisplayOptions('form', [
'type' => 'textfield',
'weight' => 5,
])
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);
$fields['tax_ca_pst_mb_number'] = BaseFieldDefinition::create('string')
->setLabel(t('Manitoba PST #'))
->setDisplayOptions('form', [
'type' => 'textfield',
'weight' => 5,
])
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);
$fields['tax_ca_qst_number'] = BaseFieldDefinition::create('string')
->setLabel(t('Quebec QST #'))
->setDisplayOptions('form', [
'type' => 'textfield',
'weight' => 5,
])
->setDisplayConfigurable('view', TRUE)
->setDisplayConfigurable('form', TRUE);

return $fields;
}

}
3 changes: 2 additions & 1 deletion modules/tax/src/Plugin/Commerce/TaxType/EuropeanUnionVat.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_order\Entity\OrderItemInterface;
use Drupal\commerce_store\Entity\StoreInterface;
use Drupal\commerce_tax\TaxableType;
use Drupal\commerce_tax\TaxZone;
use Drupal\Core\Form\FormStateInterface;
Expand Down Expand Up @@ -33,7 +34,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
/**
* {@inheritdoc}
*/
protected function resolveZones(OrderItemInterface $order_item, ProfileInterface $customer_profile) {
protected function resolveZones(OrderItemInterface $order_item, ProfileInterface $customer_profile, StoreInterface $store) {
$zones = $this->getZones();
$customer_address = $customer_profile->address->first();
$customer_country = $customer_address->getCountryCode();
Expand Down
21 changes: 13 additions & 8 deletions modules/tax/src/Plugin/Commerce/TaxType/LocalTaxTypeBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public function shouldRound() {
*/
public function applies(OrderInterface $order) {
$store = $order->getStore();
return $this->matchesAddress($store) || $this->matchesRegistrations($store);
return $this->matchesAddress($store) || $this->matchesRegistrations($store, $order);
}

/**
Expand All @@ -111,7 +111,8 @@ public function apply(OrderInterface $order) {
}

$adjustments = $order_item->getAdjustments();
$rates = $this->resolveRates($order_item, $customer_profile);
$rates = $this->resolveRates($order_item, $customer_profile, $store);

// Don't overcharge a tax-exempt customer if the price is tax-inclusive.
// A negative adjustment is added with the difference, and optionally
// applied to the unit price in the TaxOrderProcessor.
Expand All @@ -128,7 +129,7 @@ public function apply(OrderInterface $order) {
});
if (empty($positive_tax_adjustments)) {
$store_profile = $this->buildStoreProfile($store);
$rates = $this->resolveRates($order_item, $store_profile);
$rates = $this->resolveRates($order_item, $store_profile, $store);
$negate = TRUE;
}
}
Expand Down Expand Up @@ -210,7 +211,7 @@ protected function matchesAddress(StoreInterface $store) {
*/
protected function matchesRegistrations(StoreInterface $store) {
foreach ($this->getZones() as $zone) {
if ($this->checkRegistrations($store, $zone)) {
if ($zone->isRegistered($store) && $this->checkRegistrations($store, $zone)) {
return TRUE;
}
}
Expand Down Expand Up @@ -244,13 +245,15 @@ protected function checkRegistrations(StoreInterface $store, TaxZone $zone) {
* The order item.
* @param \Drupal\profile\Entity\ProfileInterface $customer_profile
* The customer profile. Contains the address and tax number.
* @param \Drupal\commerce_store\Entity\StoreInterface $store
* The store.
*
* @return \Drupal\commerce_tax\TaxRate[]
* The tax rates, keyed by tax zone ID.
*/
protected function resolveRates(OrderItemInterface $order_item, ProfileInterface $customer_profile) {
protected function resolveRates(OrderItemInterface $order_item, ProfileInterface $customer_profile, StoreInterface $store) {
$rates = [];
$zones = $this->resolveZones($order_item, $customer_profile);
$zones = $this->resolveZones($order_item, $customer_profile, $store);
foreach ($zones as $zone) {
$rate = $this->chainRateResolver->resolve($zone, $order_item, $customer_profile);
if (is_object($rate)) {
Expand All @@ -267,15 +270,17 @@ protected function resolveRates(OrderItemInterface $order_item, ProfileInterface
* The order item.
* @param \Drupal\profile\Entity\ProfileInterface $customer_profile
* The customer profile. Contains the address and tax number.
* @param \Drupal\commerce_store\Entity\StoreInterface $store
* The store.
*
* @return \Drupal\commerce_tax\TaxZone[]
* The tax zones.
*/
protected function resolveZones(OrderItemInterface $order_item, ProfileInterface $customer_profile) {
protected function resolveZones(OrderItemInterface $order_item, ProfileInterface $customer_profile, StoreInterface $store) {
$customer_address = $customer_profile->get('address')->first();
$resolved_zones = [];
foreach ($this->getZones() as $zone) {
if ($zone->match($customer_address)) {
if ($zone->match($customer_address) && $zone->isRegistered($store)) {
$resolved_zones[] = $zone;
}
}
Expand Down
7 changes: 7 additions & 0 deletions modules/tax/src/Plugin/Commerce/TaxType/TaxTypeBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,11 @@ protected function buildStoreProfile(StoreInterface $store) {
return $this->storeProfiles[$store_id];
}

/**
* {@inheritdoc}
*/
public function storeFields() {
return [];
}

}
8 changes: 8 additions & 0 deletions modules/tax/src/Plugin/Commerce/TaxType/TaxTypeInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,12 @@ public function applies(OrderInterface $order);
*/
public function apply(OrderInterface $order);

/**
* Generate store fields specific to this tax type.
*
* @return array
* An array of fields to add to the store entity type.
*/
public function storeFields();

}
24 changes: 24 additions & 0 deletions modules/tax/src/TaxZone.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use CommerceGuys\Addressing\AddressInterface;
use CommerceGuys\Addressing\Zone\ZoneTerritory;
use Drupal\commerce_store\Entity\StoreInterface;

/**
* Represents a tax zone.
Expand Down Expand Up @@ -45,6 +46,13 @@ class TaxZone {
*/
protected $rates;

/**
* Field on the Store holding registration data.
*
* @var string
*/
protected $registration;

/**
* Constructs a new TaxZone instance.
*
Expand Down Expand Up @@ -72,6 +80,9 @@ public function __construct(array $definition) {
foreach ($definition['rates'] as $rate_definition) {
$this->rates[] = new TaxRate($rate_definition);
}
if (isset($definition['registration'])) {
$this->registration = $definition['registration'];
}
}

/**
Expand Down Expand Up @@ -144,4 +155,17 @@ public function match(AddressInterface $address) {
return FALSE;
}

/**
* Checks if the zone is registered if a registration field is specificed.
*
* @return bool
* Whether the zone is registered.
*/
public function isRegistered(StoreInterface $store) {
if (isset($this->registration)) {
return (bool) $store->get($this->registration)->value;
}
return TRUE;
}

}