PageRenderTime 59ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/IsotopeGermanize.php

https://github.com/katgirl/isotope_germanize
PHP | 977 lines | 617 code | 178 blank | 182 comment | 139 complexity | 2ed2a9bb3f19e1b1e24f9b4c14e41d26 MD5 | raw file
  1. <?php if (!defined('TL_ROOT')) die('You can not access this file directly!');
  2. /**
  3. * Provides several functionality for German shops:
  4. * VAT-handling, gross- and net-prices, tax-notes at several places
  5. *
  6. * This extension depends on the Contao-Extension Isotope eCommerce
  7. *
  8. * @copyright 2013 de la Haye Kommunikationsdesign <http://www.delahaye.de>
  9. * @author Christian de la Haye <service@delahaye.de>
  10. * @author Andreas Schempp <andreas.schempp@terminal42.ch>
  11. * @package isotope_germanize
  12. * @license LGPL
  13. * @filesource
  14. */
  15. /**
  16. * German shop functionality
  17. *
  18. * @package isotope_germanize
  19. * @author Christian de la Haye <service@delahaye.de>
  20. * @author Andreas Schempp <andreas.schempp@terminal42.ch>
  21. */
  22. class IsotopeGermanize extends IsotopeFrontend
  23. {
  24. protected static $arrEuCountries = array('at', 'be', 'bg', 'cy', 'cz', 'de', 'dk', 'es', 'fi', 'fr', 'gb', 'gr', 'hu', 'ie', 'it', 'je', 'lt', 'lu', 'lv', 'mt', 'nl', 'pl', 'pt', 'ro', 'se', 'si', 'sk');
  25. protected static $strHost = 'evatr.bff-online.de';
  26. protected $arrCheckData = array();
  27. /**
  28. * Overwrite the default Isotope tax calculation if german store config is active
  29. * @param Database_Result
  30. * @param float
  31. * @param bool
  32. * @param array
  33. * @return mixed
  34. */
  35. public function calculateTax($objTaxClass, $fltPrice, $blnAdd, $arrAddresses)
  36. {
  37. // Trigger default tax calculation
  38. if (!IsotopeGermanize::isActive()) {
  39. return false;
  40. }
  41. // Calculate a product price (add or remove tax)
  42. if (!$blnAdd) {
  43. if ($objTaxClass->germanize_price == 'gross' && (self::hasTaxFreePrices() || self::hasNetPrices())) {
  44. return $fltPrice - $this->calculateTaxIncluded($fltPrice, $objTaxClass->germanize_rate);
  45. } elseif ($objTaxClass->germanize_price == 'net' && self::hasGrossPrices()) {
  46. return $fltPrice + $this->calculateTaxSurcharge($fltPrice, $objTaxClass->germanize_rate);
  47. }
  48. return $fltPrice;
  49. }
  50. // Calculate tax surcharges
  51. else {
  52. if (self::hasNetPrices()) {
  53. $strPercent = $this->getTaxPercentForRate($objTaxClass->germanize_rate);
  54. $fltTax = $this->calculateTaxSurcharge($fltPrice, $objTaxClass->germanize_rate);
  55. return array($objTaxClass->id => array
  56. (
  57. 'label' => $GLOBALS['TL_LANG']['iso_germanize']['vatCart']['net'],
  58. 'price' => $strPercent,
  59. 'total_price' => Isotope::getInstance()->roundPrice($fltTax, $objTaxClass->applyRoundingIncrement),
  60. 'add' => true,
  61. ));
  62. } elseif (self::hasGrossPrices()) {
  63. $strPercent = $this->getTaxPercentForRate($objTaxClass->germanize_rate);
  64. $fltTax = $this->calculateTaxIncluded($fltPrice, $objTaxClass->germanize_rate);
  65. return array($objTaxClass->id => array
  66. (
  67. 'label' => $GLOBALS['TL_LANG']['iso_germanize']['vatCart']['gross'],
  68. 'price' => $strPercent,
  69. 'total_price' => Isotope::getInstance()->roundPrice($fltTax, $objTaxClass->applyRoundingIncrement),
  70. 'add' => false,
  71. ));
  72. }
  73. return array();
  74. }
  75. }
  76. /**
  77. * Return true if german tax management is active
  78. * @return bool
  79. */
  80. public static function isActive()
  81. {
  82. return (bool) Isotope::getInstance()->Config->germanize;
  83. }
  84. /**
  85. * Return true if user is on a checkout page
  86. * @return bool
  87. */
  88. public static function isOnCheckoutPage()
  89. {
  90. global $objPage;
  91. $arrPages = deserialize(Isotope::getInstance()->Config->checkout_pages, true);
  92. return in_array($objPage->id, $arrPages);
  93. }
  94. /**
  95. * Return true if user is in germany or country is unknown
  96. * @return bool
  97. */
  98. public static function isGermany()
  99. {
  100. $strCountry = Isotope::getInstance()->Cart->shippingAddress->country;
  101. return ($strCountry == 'de' || $strCountry == '');
  102. }
  103. /**
  104. * Return true if address is in the EU
  105. * @return bool
  106. */
  107. public static function isEuropeanUnion()
  108. {
  109. return in_array(Isotope::getInstance()->Cart->shippingAddress->country, self::$arrEuCountries);
  110. }
  111. /**
  112. * Return true if user is in a group with net prices
  113. * @return bool
  114. */
  115. public static function hasNetPriceGroup()
  116. {
  117. if (FE_USER_LOGGED_IN !== true) {
  118. return false;
  119. }
  120. $arrUserGroups = FrontendUser::getInstance()->groups;
  121. $arrNetGroups = deserialize(Isotope::getInstance()->Config->netprice_groups);
  122. if (!is_array($arrUserGroups) || !is_array($arrNetGroups)) {
  123. return false;
  124. }
  125. return count(array_intersect($arrUserGroups, $arrNetGroups)) > 0;
  126. }
  127. /**
  128. * Return true if a vat-no exists
  129. * @return bool
  130. */
  131. public static function hasVatNo()
  132. {
  133. return Isotope::getInstance()->Cart->shippingAddress->vat_no != '';
  134. }
  135. /**
  136. * Return true if a valid vat-no exists
  137. * @return bool
  138. */
  139. public static function hasValidVatNo()
  140. {
  141. // in test mode we always have a valif vat no
  142. if($GLOBALS['isotope_germanize']['testmode'])
  143. {
  144. return true;
  145. }
  146. if (!self::hasVatNo()
  147. || (FE_USER_LOGGED_IN !== true && !Isotope::getInstance()->Config->vatcheck_guests)
  148. || (Isotope::getInstance()->Cart->shippingAddress->vat_no_ok != 'ok_qualified' && Isotope::getInstance()->Cart->shippingAddress->vat_no_ok != 'ok_manual')) {
  149. return false;
  150. }
  151. return true;
  152. }
  153. /**
  154. * Return true if all prices have to be tax free
  155. * @return bool
  156. */
  157. public static function hasTaxFreePrices()
  158. {
  159. $blnGuestCheck = (FE_USER_LOGGED_IN === true || self::isOnCheckoutPage());
  160. // Situation 1
  161. if ($blnGuestCheck && !self::isEuropeanUnion()) {
  162. return true;
  163. }
  164. // Situation 2
  165. if ($blnGuestCheck && self::isEuropeanUnion() && !self::isGermany() && self::hasValidVatNo()) {
  166. return true;
  167. }
  168. return false;
  169. }
  170. /**
  171. * Return true if net prices are active
  172. * @return bool
  173. */
  174. public static function hasNetPrices()
  175. {
  176. if (self::hasTaxFreePrices() || !self::hasNetPriceGroup()) {
  177. return false;
  178. }
  179. if (!self::isOnCheckoutPage() || self::isEuropeanUnion()) {
  180. return true;
  181. }
  182. return false;
  183. }
  184. /**
  185. * Return true if gross prices are active
  186. * @return bool
  187. */
  188. public function hasGrossPrices()
  189. {
  190. return (!self::hasTaxFreePrices() && !self::hasNetPrices());
  191. }
  192. /**
  193. * Return a nice formatted string with a tax rate
  194. * @return string
  195. */
  196. public static function getTaxPercentForRate($fltRate)
  197. {
  198. $arrFormat = $GLOBALS['ISO_NUM'][Isotope::getInstance()->Config->currencyFormat];
  199. return number_format($fltRate, (floor($fltRate)==$fltRate ? 0 : 1), $arrFormat[1], $arrFormat[2]).'%';
  200. }
  201. /**
  202. * Return gross price
  203. * @return float
  204. */
  205. protected function calculateTaxIncluded($fltPrice, $fltRate)
  206. {
  207. return $fltPrice - ($fltPrice / (1 + ($fltRate / 100)));
  208. }
  209. /**
  210. * Return net price
  211. * @return float
  212. */
  213. protected function calculateTaxSurcharge($fltPrice, $fltRate)
  214. {
  215. return ($fltPrice * (1 + ($fltRate / 100))) - $fltPrice;
  216. }
  217. /**
  218. * Return a notice about the vat status of the order, is displayed in the cart and checkout
  219. * @return string
  220. */
  221. public static function getTaxNotice()
  222. {
  223. $objAddress = Isotope::getInstance()->Cart->shippingAddress;
  224. $arrCountries = Isotope::getInstance()->call('getCountries');
  225. $strCountry = $arrCountries[$objAddress->country];
  226. $b = sprintf($GLOBALS['TL_LANG']['iso_germanize']['notes']['nonEuGuest'], $strCountry);
  227. $c = sprintf($GLOBALS['TL_LANG']['iso_germanize']['notes']['nonEu'], $strCountry);
  228. $d = sprintf($GLOBALS['TL_LANG']['iso_germanize']['notes']['confirmedVatNo'], $objAddress->vat_no);
  229. $e = sprintf($GLOBALS['TL_LANG']['iso_germanize']['notes']['unconfirmedVatNo'], $objAddress->vat_no, $strCountry);
  230. $f = sprintf($GLOBALS['TL_LANG']['iso_germanize']['notes']['noVatNo'], $strCountry);
  231. if (self::isGermany()) {
  232. return '';
  233. }
  234. if (!self::isOnCheckoutPage()) {
  235. if (FE_USER_LOGGED_IN === true && !self::isEuropeanUnion()) {
  236. return $c;
  237. } elseif (FE_USER_LOGGED_IN === true && self::isEuropeanUnion() && self::hasValidVatNo()) {
  238. return $d;
  239. } elseif (self::hasNetPriceGroup()) {
  240. if (self::isEuropeanUnion() && self::hasVatNo() && !self::hasValidVatNo()) {
  241. return $e;
  242. } else {
  243. return $b;
  244. }
  245. } elseif (self::isEuropeanUnion()) {
  246. return $f;
  247. } else {
  248. return $b;
  249. }
  250. } else {
  251. if (!self::isEuropeanUnion()) {
  252. return $c;
  253. } elseif (self::isEuropeanUnion() && self::hasValidVatNo()) {
  254. return $d;
  255. } elseif (self::isEuropeanUnion() && self::hasVatNo()) {
  256. return $e;
  257. } else {
  258. return '';
  259. }
  260. }
  261. }
  262. /**
  263. * Inject notes in default templates automatically
  264. * @param string
  265. * @param string
  266. * @return string
  267. */
  268. public function injectNotes($strBuffer, $strTemplate)
  269. {
  270. // Use only if the shop is defined as German
  271. if (!IsotopeGermanize::isActive()) {
  272. return $strBuffer;
  273. }
  274. switch($strTemplate)
  275. {
  276. case 'iso_list_default':
  277. case 'iso_list_variants':
  278. case 'iso_reader_default':
  279. // Pricing note at the product
  280. if(strpos($strBuffer,'isotopeGerman::notePricing')===false)
  281. {
  282. // Inject the pricing note after the baseprice or the price
  283. $strSearchTag = 'price';
  284. if(strpos($strBuffer,'class="baseprice')!==false)
  285. {
  286. $strSearchTag = 'baseprice';
  287. }
  288. return preg_replace('#\<div class="'.$strSearchTag.'">(.*)\</div>(.*)#Uis', '<div class="'.$strSearchTag.'">\1</div>'.$this->isotopeGermanizeInsertTags('isotopeGerman::notePricing').'\2', $strBuffer);
  289. }
  290. break;
  291. case 'iso_cart_full':
  292. // VAT note in the main cart
  293. if(strpos($strBuffer,'isotopeGerman::noteVat')===false)
  294. {
  295. $strBuffer = str_replace('<div class="submit_container">',$this->isotopeGermanizeInsertTags('isotopeGerman::noteVat').'<div class="submit_container">',$strBuffer);
  296. }
  297. // shipping note in the main cart
  298. if(strpos($strBuffer,'isotopeGerman::noteShipping')===false)
  299. {
  300. $strBuffer = str_replace('<div class="submit_container">',$this->isotopeGermanizeInsertTags('isotopeGerman::noteShipping').'<div class="submit_container">',$strBuffer);
  301. }
  302. return $strBuffer;
  303. break;
  304. case 'iso_checkout_shipping_method':
  305. case 'iso_checkout_payment_method':
  306. // VAT note in the other checkout steps
  307. if(strpos($strBuffer,'isotopeGerman::noteVat')===false)
  308. {
  309. return $strBuffer.$this->isotopeGermanizeInsertTags('isotopeGerman::noteVat');
  310. }
  311. break;
  312. case 'iso_checkout_order_products':
  313. // VAT note in the checkout product overview (has to be above the products/total)
  314. if(strpos($strBuffer,'isotopeGerman::noteVat')===false)
  315. {
  316. return $this->isotopeGermanizeInsertTags('isotopeGerman::noteVat').$strBuffer;
  317. }
  318. break;
  319. }
  320. return $strBuffer;
  321. }
  322. /**
  323. * Inject notes via insert tags
  324. * @param string
  325. * @return string
  326. */
  327. public function isotopeGermanizeInsertTags($strTag)
  328. {
  329. // Use only if the shop is defined as German
  330. if (!IsotopeGermanize::isActive()) {
  331. return $strTag;
  332. }
  333. $arrTag = trimsplit('::', $strTag);
  334. if($arrTag[0] == 'isotopeGerman')
  335. {
  336. switch($arrTag[1])
  337. {
  338. case 'noteShipping':
  339. return '<div class="noteShipping">'.self::getShippingNotice().'</div>';
  340. break;
  341. case 'notePricing':
  342. // [2]: # tax class, [3]: shipping exempt, [4]: txt for text version
  343. return '<div class="notePricing">'.$this->getPriceNotice($arrTag[2], $arrTag[3], $arrTag[4]).'</div>';
  344. break;
  345. case 'noteVat':
  346. return '<div class="noteVat">'.self::getTaxNotice().'</div>';
  347. break;
  348. }
  349. }
  350. }
  351. /**
  352. * Return the shipping notice built from an article
  353. * @return string
  354. */
  355. protected function getShippingNotice()
  356. {
  357. return Isotope::getInstance()->Config->shipping_note ? Controller::replaceInsertTags('{{insert_article::'.Isotope::getInstance()->Config->shipping_note.'}}') : false;
  358. }
  359. /**
  360. * Return a price notice to be displayed at a single product
  361. * @return string
  362. */
  363. protected function getPriceNotice($intTaxClass=false, $blnShippingExempt=false, $txt=false)
  364. {
  365. if ((FE_USER_LOGGED_IN === true && !self::isEuropeanUnion()) || (FE_USER_LOGGED_IN === true && !self::isGermany() && self::isEuropeanUnion() && self::hasValidVatNo())) {
  366. if (!$blnShippingExempt)
  367. {
  368. $strNote = $GLOBALS['TL_LANG']['iso_germanize']['priceNotes']['taxfree_shipping'];
  369. $strShippingLink = $this->getShippingLink();
  370. $note = $strShippingLink ? str_replace('<a>',$strShippingLink, $strNote) : str_replace('<a>','',str_replace('</a>','', $strNote));
  371. } else {
  372. $strNote = $GLOBALS['TL_LANG']['iso_germanize']['priceNotes']['taxfree_noShipping'];
  373. }
  374. } elseif (self::hasNetPriceGroup()) {
  375. $strTax = $this->getTaxRate($intTaxClass);
  376. $strTax .= $strTax ? ' ' : '';
  377. if (!$blnShippingExempt)
  378. {
  379. $strNote = $GLOBALS['TL_LANG']['iso_germanize']['priceNotes']['net_shipping'];
  380. $strShippingLink = $this->getShippingLink();
  381. $strNote = sprintf(($strShippingLink ? str_replace('<a>',$strShippingLink, $strNote) : str_replace('<a>','',str_replace('</a>','', $strNote))), $strTax);
  382. } else {
  383. $strNote = sprintf($GLOBALS['TL_LANG']['iso_germanize']['priceNotes']['net_noShipping'], $strTax);
  384. }
  385. } else {
  386. $strTax = $this->getTaxRate($intTaxClass);
  387. $strTax .= $strTax ? ' ' : '';
  388. if (!$blnShippingExempt)
  389. {
  390. $strNote = $GLOBALS['TL_LANG']['iso_germanize']['priceNotes']['gross_shipping'];
  391. $strShippingLink = $this->getShippingLink();
  392. $strNote = sprintf(($strShippingLink ? str_replace('<a>',$strShippingLink, $strNote) : str_replace('<a>','',str_replace('</a>','', $strNote))), $strTax);
  393. } else {
  394. $strNote = sprintf($GLOBALS['TL_LANG']['iso_germanize']['priceNotes']['gross_noShipping'], $strTax);
  395. }
  396. }
  397. // Optional parameter txt for text only
  398. if($txt)
  399. {
  400. $strNote = trim(strip_tags($strNote));
  401. }
  402. return $strNote;
  403. }
  404. /**
  405. * Return text for a tax rate
  406. * @return string
  407. */
  408. protected function getTaxRate($intTaxClass)
  409. {
  410. if(!is_numeric($intTaxClass))
  411. {
  412. return false;
  413. }
  414. $objRate = Database::getInstance()->prepare("SELECT germanize_rate FROM tl_iso_tax_class WHERE id=?")
  415. ->limit(1)
  416. ->execute($intTaxClass);
  417. if($objRate->numRows)
  418. {
  419. return $this->getTaxPercentForRate($objRate->germanize_rate);
  420. }
  421. return false;
  422. }
  423. /**
  424. * Return a link to the shipping costs page
  425. * @return string
  426. */
  427. protected function getShippingLink()
  428. {
  429. global $objPage;
  430. if(Isotope::getInstance()->Config->shipping_page < 1)
  431. {
  432. return false;
  433. }
  434. // Build link to the shipping costs page
  435. $objTarget = $this->getPageDetails(Isotope::getInstance()->Config->shipping_page);
  436. if ($GLOBALS['TL_CONFIG']['addLanguageToUrl'])
  437. {
  438. $strUrl = $this->generateFrontendUrl($objTarget->row(), null, $objTarget->rootLanguage);
  439. }
  440. else
  441. {
  442. $strUrl = $this->generateFrontendUrl($objTarget->row());
  443. }
  444. $strLink = '<a href="'.$strUrl.'"';
  445. if (strncmp(Isotope::getInstance()->Config->shipping_rel, 'lightbox', 8) !== 0 || $objPage->outputFormat == 'xhtml')
  446. {
  447. $strLink .= ' rel="'. Isotope::getInstance()->Config->shipping_rel .'"';
  448. }
  449. else
  450. {
  451. $strLink .= ' data-lightbox="'. Isotope::getInstance()->Config->shipping_rel .'"';
  452. }
  453. if(Isotope::getInstance()->Config->shipping_target)
  454. {
  455. $strLink .= ($objPage->outputFormat == 'xhtml') ? ' onclick="return !window.open(this.href)"' : ' target="_blank"';
  456. }
  457. $strLink .= '>';
  458. return $strLink;
  459. }
  460. /**
  461. * Check vat no and update the status
  462. */
  463. public function updateVatCheck()
  464. {
  465. // Set the data to check as it is not yet stored in the object
  466. $addrType = (Isotope::getInstance()->Cart->shippingAddress_id != -1 ? 'shipping_address' : 'billing_address');
  467. $this->arrCheckData = array(
  468. 'addr_type' => $addrType,
  469. 'company' => Input::getInstance()->post($addrType.'_company'),
  470. 'street' => Input::getInstance()->post($addrType.'_street_1'),
  471. 'postal' => Input::getInstance()->post($addrType.'_postal'),
  472. 'city' => Input::getInstance()->post($addrType.'_city'),
  473. 'country' => Input::getInstance()->post($addrType.'_country'),
  474. 'vat_no' => Input::getInstance()->post($addrType.'_vat_no')
  475. );
  476. // check the vat no
  477. if($arrCheck = $this->checkVatNo())
  478. {
  479. // update member data
  480. $this->updateMember($arrCheck['status']);
  481. // mails for the vendor
  482. $this->sendMails($arrCheck);
  483. // log the action
  484. $this->logIt($arrCheck['status']);
  485. // update the session data
  486. Input::getInstance()->setPost($this->arrCheckData['addr_type'].'_vat_no_ok',$arrCheck['status']);
  487. }
  488. }
  489. /**
  490. * Return array with results of the online check
  491. * @return array
  492. */
  493. protected function checkVatNo()
  494. {
  495. if(!Isotope::getInstance()->Config->vat_no
  496. || self::isGermany()
  497. || !self::isEuropeanUnion()
  498. || (!FE_USER_LOGGED_IN && !Isotope::getInstance()->Config->vatcheck_guests)
  499. || (FE_USER_LOGGED_IN && !Isotope::getInstance()->Config->vatcheck_member)
  500. || !$this->addressHasbeenModified()
  501. )
  502. {
  503. return false;
  504. }
  505. // Formal check vat_no
  506. if(!$strCustomerVatNo = self::preCheckVatNo($this->arrCheckData['vat_no']))
  507. {
  508. return array(
  509. 'status' => 'nok',
  510. 'error' => 'vat_no'
  511. );
  512. }
  513. // Formal check own vat_no
  514. if(!$strOwnVatNo = self::preCheckVatNo(Isotope::getInstance()->Config->vat_no))
  515. {
  516. return array(
  517. 'status' => 'nok_invalid',
  518. 'error' => 'own_vat_no'
  519. );
  520. }
  521. // Server is online
  522. if(!self::testConnection())
  523. {
  524. return array(
  525. 'status' => 'nok_invalid',
  526. 'error' => 'server'
  527. );
  528. }
  529. // Verify the vat no online
  530. $arrCheck = $this->verifyVatNo($strCustomerVatNo, $strOwnVatNo);
  531. // don't activate if no member groups shall be auto-activated
  532. $arrCheck['status'] = ($arrCheck['status'] == 'ok_qualified' && FE_USER_LOGGED_IN && count(array_intersect(Isotope::getInstance()->Config->vatcheck_groups, FrontendUser::getInstance()->groups)) < 1) ? 'nok_qualified' : $arrCheck['status'];
  533. return $arrCheck;
  534. }
  535. /**
  536. * Verify the vat no online
  537. * @return array
  538. */
  539. protected function verifyVatNo($strCustomerVatNo, $strOwnVatNo)
  540. {
  541. require_once(TL_ROOT.'/system/modules/isotope_germanize/IXR_Library.php');
  542. $client = new IXR_Client('https://'.self::$strHost);
  543. $UstId_1 = strtoupper($strOwnVatNo);
  544. $UstId_2 = strtoupper($strCustomerVatNo);
  545. $Firmenname = $this->arrCheckData['company'];
  546. $Ort = $this->arrCheckData['city'];
  547. $PLZ = $this->arrCheckData['postal'];
  548. $Strasse = $this->arrCheckData['street'];
  549. $Druck = $GLOBALS['isotope_germanize']['order_printed_verification'] ? 'ja' : 'nein';
  550. if (!$client->query('evatrRPC',
  551. $UstId_1,
  552. $UstId_2,
  553. $Firmenname,
  554. $Ort,
  555. $PLZ,
  556. $Strasse,
  557. $Druck))
  558. {
  559. return array(
  560. 'status' => 'nok_invalid',
  561. 'error' => 'server',
  562. 'check_code' => $client->getErrorCode().': '.$client->getErrorMessage()
  563. );
  564. }
  565. $xml = $client->getResponse();
  566. preg_match('#(?<=ErrorCode</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrErrorCode);
  567. preg_match('#(?<=Datum</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrDatum);
  568. preg_match('#(?<=Uhrzeit</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrUhrzeit);
  569. preg_match('#(?<=Erg_Name</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrErg_Name);
  570. preg_match('#(?<=Erg_Str</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrErg_Str);
  571. preg_match('#(?<=Erg_PLZ</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrErg_PLZ);
  572. preg_match('#(?<=Erg_Ort</string></value>\n<value><string>).*(?=</string.*)#', $xml, $arrErg_Ort);
  573. $arrResponse = array(
  574. 'check_date' => $GLOBALS['TL_LANG']['iso_germanize']['bff'][$arrDatum[0]],
  575. 'check_time' => $GLOBALS['TL_LANG']['iso_germanize']['bff'][$arrUhrzeit[0]],
  576. 'check_company' => $arrErg_Name[0] ? $GLOBALS['TL_LANG']['iso_germanize']['bff'][$arrErg_Name[0]] : '',
  577. 'check_street' => $arrErg_Str[0] ? $GLOBALS['TL_LANG']['iso_germanize']['bff'][$arrErg_Str[0]] : '',
  578. 'check_postal' => $arrErg_PLZ[0] ? $GLOBALS['TL_LANG']['iso_germanize']['bff'][$arrErg_PLZ[0]] : '',
  579. 'check_city' => $arrErg_Ort[0] ? $GLOBALS['TL_LANG']['iso_germanize']['bff'][$arrErg_Ort[0]] : '',
  580. 'check_code' => $arrErrorCode[0]
  581. );
  582. switch($arrErrorCode[0])
  583. {
  584. // ok
  585. case '200':
  586. if(($arrErg_Name[0] != 'A' && $arrErg_Name[0] != 'D')
  587. || ($arrErg_Str[0] != 'A' && $arrErg_Str[0] != 'D' && !$GLOBALS['isotope_germanize']['loose_verification_street'])
  588. || ($arrErg_PLZ[0] != 'A' && $arrErg_PLZ[0] != 'D' && !$GLOBALS['isotope_germanize']['loose_verification_postal'])
  589. || ($arrErg_Ort[0] != 'A' && $arrErg_Ort[0] != 'D')
  590. )
  591. {
  592. // the vat no doesn't fit the address
  593. return array_merge($arrResponse, array(
  594. 'status' => 'nok_invalid',
  595. 'error' => 'general'
  596. ));
  597. }
  598. // everything is fine
  599. return array_merge($arrResponse, array(
  600. 'status' => 'ok_qualified'
  601. ));
  602. break;
  603. // only simple verification available
  604. case '216':
  605. case '218':
  606. case '219':
  607. return array_merge($arrResponse, array(
  608. 'status' => 'nok_simple',
  609. 'error' => 'general'
  610. ));
  611. break;
  612. // (technical) error
  613. default:
  614. return array_merge($arrResponse, array(
  615. 'status' => 'nok_invalid',
  616. 'error' => 'general'
  617. ));
  618. break;
  619. }
  620. }
  621. /**
  622. * Test connection to authority server (is only available 5:00 - 22:00
  623. * @return array
  624. */
  625. public static function testConnection()
  626. {
  627. return is_array(gethostbynamel(self::$strHost)) ? true : false;
  628. }
  629. /**
  630. * Log the action
  631. */
  632. protected function logIt($strStatus)
  633. {
  634. switch($strStatus)
  635. {
  636. case 'ok_qualified':
  637. case 'nok_qualified':
  638. case 'nok_simple':
  639. $this->log('VAT-ID '.$this->arrCheckData['vat_no'].' ('.(FE_USER_LOGGED_IN ? 'User '.FrontendUser::getInstance()->id : 'Guest').'): '.$GLOBALS['TL_LANG']['iso_germanize'][$strStatus],'updateVatCheck','VAT-CHECK');
  640. break;
  641. default:
  642. $this->log('VAT-ID '.$this->arrCheckData['vat_no'].' ('.(FE_USER_LOGGED_IN ? 'User '.FrontendUser::getInstance()->id : 'Guest').'): '.$GLOBALS['TL_LANG']['iso_germanize'][$strStatus],'updateVatCheck','ERROR');
  643. break;
  644. }
  645. }
  646. /**
  647. * Update the member data if address and vat_no match the cart version
  648. */
  649. protected function updateMember($strStatus)
  650. {
  651. if(!FE_USER_LOGGED_IN)
  652. {
  653. return false;
  654. }
  655. $arrMember = array(
  656. FrontendUser::getInstance()->company,
  657. FrontendUser::getInstance()->street,
  658. FrontendUser::getInstance()->postal,
  659. FrontendUser::getInstance()->city,
  660. FrontendUser::getInstance()->country
  661. );
  662. $arrAddress = array(
  663. $this->arrCheckData['company'],
  664. $this->arrCheckData['street'],
  665. $this->arrCheckData['postal'],
  666. $this->arrCheckData['city'],
  667. $this->arrCheckData['country']
  668. );
  669. if($arrMember != $arrAddress)
  670. {
  671. return false;
  672. }
  673. Database::getInstance()->prepare("UPDATE tl_member SET vat_no=?, vat_no_ok=? WHERE id=?")
  674. ->executeUncached($this->arrCheckData['vat_no'], $strStatus, FrontendUser::getInstance()->id);
  675. return true;
  676. }
  677. /**
  678. * Confirmation mails
  679. */
  680. protected function sendMails($arrCheck)
  681. {
  682. $arrCountries = $this->getCountries();
  683. $arrMailfields = array(
  684. 'host' => self::$strHost,
  685. 'status' => $GLOBALS['TL_LANG']['iso_germanize'][$arrCheck['status']],
  686. 'error' => $GLOBALS['TL_LANG']['iso_germanize']['error'][$arrCheck['error']],
  687. 'vat_no' => $this->arrCheckData['vat_no'],
  688. 'date' => date($GLOBALS['TL_CONFIG']['datimFormat'],time()),
  689. 'company' => $this->arrCheckData['company'],
  690. 'street' => $this->arrCheckData['street'],
  691. 'postal' => $this->arrCheckData['postal'],
  692. 'city' => $this->arrCheckData['city'],
  693. 'country' => $arrCountries[$this->arrCheckData['country']],
  694. 'member_id' => (FE_USER_LOGGED_IN ? FrontendUser::getInstance()->id : $GLOBALS['TL_LANG']['iso_germanize']['guest_order']),
  695. 'address_id' => (Isotope::getInstance()->Cart->shippingAddress_id > 0 ? Isotope::getInstance()->Cart->shippingAddress_id : ''),
  696. 'check_date' => $arrCheck['check_date'],
  697. 'check_time' => $arrCheck['check_time'],
  698. 'check_company' => $arrCheck['check_company'],
  699. 'check_street' => $arrCheck['check_street'],
  700. 'check_postal' => $arrCheck['check_postal'],
  701. 'check_city' => $arrCheck['check_city'],
  702. 'check_code' => $arrCheck['check_code']
  703. );
  704. if($arrCheck['status'] == 'ok_qualified' || $arrCheck['status'] == 'nok_qualified')
  705. {
  706. $arrMailfields['inactive'] = $arrCheck['status'] == 'nok_qualified' ? $GLOBALS['TL_LANG']['iso_germanize']['inactive'] : '';
  707. $objEmail = new Email();
  708. $objEmail->subject = $GLOBALS['TL_LANG']['iso_germanize']['mail_verfication_subject'];
  709. $objEmail->text = $GLOBALS['TL_LANG']['iso_germanize']['mail_verfication_text'];
  710. foreach($arrMailfields as $k=>$v)
  711. {
  712. $objEmail->subject = str_replace('##'.$k.'##', $v, $objEmail->subject);
  713. $objEmail->text = str_replace('##'.$k.'##', $v, $objEmail->text);
  714. }
  715. $objEmail->sendTo(Isotope::getInstance()->Config->email);
  716. }
  717. else
  718. {
  719. $objEmail = new Email();
  720. // Reminding e-mail to the vendor for manual cheack after salte, only for members
  721. if(1==1 || FE_USER_LOGGED_IN)
  722. {
  723. $objEmail->subject = $GLOBALS['TL_LANG']['iso_germanize']['mail_reminder_subject'];
  724. $objEmail->text = $GLOBALS['TL_LANG']['iso_germanize']['mail_reminder_text'];
  725. $objEmail->subject = $GLOBALS['TL_LANG']['iso_germanize']['mail_reminder_subject'];
  726. $objEmail->text = $GLOBALS['TL_LANG']['iso_germanize']['mail_reminder_text'];
  727. foreach($arrMailfields as $k=>$v)
  728. {
  729. $objEmail->subject = str_replace('##'.$k.'##', $v, $objEmail->subject);
  730. $objEmail->text = str_replace('##'.$k.'##', $v, $objEmail->text);
  731. }
  732. $objEmail->sendTo(Isotope::getInstance()->Config->email);
  733. }
  734. }
  735. }
  736. /**
  737. * Return array with country-code and formally checked number
  738. * @return array
  739. */
  740. public static function preCheckVatNo($strVatNo)
  741. {
  742. $strVatNo = str_replace(array(
  743. chr(0),
  744. chr(9),
  745. chr(10),
  746. chr(11),
  747. chr(13),
  748. chr(23),
  749. chr(92),
  750. ' ',
  751. '.',
  752. '-',
  753. '_',
  754. '/',
  755. '>',
  756. '<',
  757. ','
  758. ), '', $strVatNo);
  759. if(strlen($strVatNo) < 8 || strlen($strVatNo) > 12)
  760. {
  761. return false;
  762. }
  763. return $strVatNo;
  764. }
  765. /**
  766. * Return true if an relevant address has been modified
  767. * @return bool
  768. */
  769. protected function addressHasbeenModified()
  770. {
  771. if(Input::getInstance()->post('FORM_SUBMIT') != 'iso_mod_checkout_address')
  772. {
  773. return false;
  774. }
  775. foreach(array('vat_no','company','street_1','postal','city','country') as $strRelevant)
  776. {
  777. if(Input::getInstance()->post($this->arrCheckData['addr_type'].'_'.$strRelevant) && Input::getInstance()->post($this->arrCheckData['addr_type'].'_'.$strRelevant) != Isotope::getInstance()->Cart->shippingAddress->$strRelevant)
  778. {
  779. $hasChanged = true;
  780. }
  781. }
  782. return $hasChanged;
  783. }
  784. /**
  785. * Inject data via mail tags
  786. * @param string
  787. * @return string
  788. */
  789. public function isotopeGermanizeOrderEmailData(IsotopeOrder $objOrder, $arrData)
  790. {
  791. return = array_merge($arrData, array
  792. (
  793. 'germ_shipping_note' => Isotope::getInstance()->Config->shipping_note,
  794. 'germ_shipping_page' => $this->getShippingLink(),
  795. 'germ_vat_no_ok' => Isotope::getInstance()->Cart->shippingAddress->vat_no_ok,
  796. 'germ_vat_no' => $this->arrCheckData['vat_no'],
  797. ));
  798. }
  799. }