/** * {@inheritdoc} */ public function resolveAmounts(TaxableInterface $taxable, Context $context) { $date = $context->getDate(); $rates = $this->resolveRates($taxable, $context); $amounts = []; foreach ($rates as $rate) { $amounts[] = $rate->getAmount($date); } return $amounts; }
/** * Checks whether the store is registered to collect taxes in the given zone. * * @param ZoneInterface $zone The zone. * @param Context $context The context containing store information. * * @return bool True if the store is registered to collect taxes in the * given zone, false otherwise. */ protected function checkStoreRegistration(ZoneInterface $zone, Context $context) { $storeRegistrations = $context->getStoreRegistrations(); foreach ($storeRegistrations as $country) { if (!isset($this->emptyAddresses[$country])) { $this->emptyAddresses[$country] = new Address($country); } if ($zone->match($this->emptyAddresses[$country])) { return true; } } return false; }
/** * {@inheritdoc} */ public function resolve(TaxableInterface $taxable, Context $context) { $taxTypes = $this->getTaxTypes(); $customerAddress = $context->getCustomerAddress(); $customerCountry = $customerAddress->getCountryCode(); $customerTaxTypes = $this->filterByAddress($taxTypes, $customerAddress); if (empty($customerTaxTypes)) { // The customer is not in the EU. return []; } $storeAddress = $context->getStoreAddress(); $storeCountry = $storeAddress->getCountryCode(); $storeTaxTypes = $this->filterByAddress($taxTypes, $storeAddress); $storeRegistrationTaxTypes = $this->filterByStoreRegistration($taxTypes, $context); if (empty($storeTaxTypes) && empty($storeRegistrationTaxTypes)) { // The store is not in the EU nor registered to collect EU VAT. return []; } $customerTaxNumber = $context->getCustomerTaxNumber(); // Since january 1st 2015 all digital services sold to EU customers // must apply the destination tax type(s). For example, an ebook sold // to Germany needs to have German VAT applied. $isDigital = $context->getDate()->format('Y') >= '2015' && !$taxable->isPhysical(); $resolvedTaxTypes = []; if (empty($storeTaxTypes) && !empty($storeRegistrationTaxTypes)) { // The store is not in the EU but is registered to collect VAT. // This VAT is only charged on B2C digital services. $resolvedTaxTypes = self::NO_APPLICABLE_TAX_TYPE; if ($isDigital && !$customerTaxNumber) { $resolvedTaxTypes = $customerTaxTypes; } } elseif ($customerTaxNumber && $customerCountry != $storeCountry) { // Intra-community supply (B2B). $icTaxType = $this->taxTypeRepository->get('eu_ic_vat'); $resolvedTaxTypes = [$icTaxType]; } elseif ($isDigital) { $resolvedTaxTypes = $customerTaxTypes; } else { // Physical products use the origin tax types, unless the store is // registered to pay taxes in the destination zone. This is required // when the total yearly transactions breach the defined threshold. // See http://www.vatlive.com/eu-vat-rules/vat-registration-threshold/ $resolvedTaxTypes = $storeTaxTypes; $customerTaxType = reset($customerTaxTypes); if ($this->checkStoreRegistration($customerTaxType->getZone(), $context)) { $resolvedTaxTypes = $customerTaxTypes; } } return $resolvedTaxTypes; }
/** * {@inheritdoc} */ public function resolve(TaxableInterface $taxable, Context $context) { $taxTypes = $this->getTaxTypes(); $results = []; foreach ($taxTypes as $taxType) { $zone = $taxType->getZone(); $customerZoneMatch = $zone->match($context->getCustomerAddress()); $storeZoneMatch = $zone->match($context->getStoreAddress()); if ($customerZoneMatch && $storeZoneMatch) { // The customer and store belong to the same zone. $results[] = $taxType; } elseif ($customerZoneMatch && $this->checkStoreRegistration($zone, $context)) { // The customer belongs to the zone, and the store is // registered to collect taxes there. $results[] = $taxType; } } return $results; }
/** * {@inheritdoc} */ public function resolve(TaxableInterface $taxable, Context $context) { $customerAddress = $context->getCustomerAddress(); $storeAddress = $context->getStoreAddress(); if ($customerAddress->getCountryCode() != 'CA' || $storeAddress->getCountryCode() != 'CA') { // The customer or the store is not in Canada. return []; } // Canadian tax types are matched by the customer address. // If the customer is from Ontario, the tax types are for Ontario. $taxTypes = $this->getTaxTypes(); $results = []; foreach ($taxTypes as $taxType) { $zone = $taxType->getZone(); if ($zone->match($customerAddress)) { $results[] = $taxType; } } return $results; }
/** * @covers ::__construct * * @uses \CommerceGuys\Tax\Resolver\Context::getCustomerAddress * @uses \CommerceGuys\Tax\Resolver\Context::getStoreAddress * @uses \CommerceGuys\Tax\Resolver\Context::getCustomerTaxNumber * @uses \CommerceGuys\Tax\Resolver\Context::getStoreRegistrations * @uses \CommerceGuys\Tax\Resolver\Context::getDate */ public function testConstructor() { $customerAddress = $this->getMockBuilder('CommerceGuys\\Addressing\\Model\\Address')->getMock(); $storeAddress = $this->getMockBuilder('CommerceGuys\\Addressing\\Model\\Address')->getMock(); $date = new \DateTime('2014-10-10'); $context = new Context($customerAddress, $storeAddress, '0123', ['DE'], $date); $this->assertSame($customerAddress, $context->getCustomerAddress()); $this->assertSame($storeAddress, $context->getStoreAddress()); $this->assertEquals('0123', $context->getCustomerTaxNumber()); $this->assertEquals(['DE'], $context->getStoreRegistrations()); $this->assertSame($date, $context->getDate()); }