Tablerate.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. /**
  7. * Shipping table rates
  8. *
  9. * @author Magento Core Team <core@magentocommerce.com>
  10. */
  11. namespace Magento\OfflineShipping\Model\ResourceModel\Carrier;
  12. use Magento\Framework\Filesystem;
  13. use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\Import;
  14. use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQuery;
  15. use Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate\RateQueryFactory;
  16. /**
  17. * @SuppressWarnings(PHPMD.TooManyFields)
  18. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  19. *
  20. * @api
  21. * @since 100.0.2
  22. */
  23. class Tablerate extends \Magento\Framework\Model\ResourceModel\Db\AbstractDb
  24. {
  25. /**
  26. * Import table rates website ID
  27. *
  28. * @var int
  29. */
  30. protected $_importWebsiteId = 0;
  31. /**
  32. * Errors in import process
  33. *
  34. * @var array
  35. */
  36. protected $_importErrors = [];
  37. /**
  38. * Count of imported table rates
  39. *
  40. * @var int
  41. */
  42. protected $_importedRows = 0;
  43. /**
  44. * Array of unique table rate keys to protect from duplicates
  45. *
  46. * @var array
  47. */
  48. protected $_importUniqueHash = [];
  49. /**
  50. * Array of countries keyed by iso2 code
  51. *
  52. * @var array
  53. */
  54. protected $_importIso2Countries;
  55. /**
  56. * Array of countries keyed by iso3 code
  57. *
  58. * @var array
  59. */
  60. protected $_importIso3Countries;
  61. /**
  62. * Associative array of countries and regions
  63. * [country_id][region_code] = region_id
  64. *
  65. * @var array
  66. */
  67. protected $_importRegions;
  68. /**
  69. * Import Table Rate condition name
  70. *
  71. * @var string
  72. */
  73. protected $_importConditionName;
  74. /**
  75. * Array of condition full names
  76. *
  77. * @var array
  78. */
  79. protected $_conditionFullNames = [];
  80. /**
  81. * @var \Magento\Framework\App\Config\ScopeConfigInterface
  82. * @since 100.1.0
  83. */
  84. protected $coreConfig;
  85. /**
  86. * @var \Psr\Log\LoggerInterface
  87. * @since 100.1.0
  88. */
  89. protected $logger;
  90. /**
  91. * @var \Magento\Store\Model\StoreManagerInterface
  92. * @since 100.1.0
  93. */
  94. protected $storeManager;
  95. /**
  96. * @var \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
  97. * @since 100.1.0
  98. */
  99. protected $carrierTablerate;
  100. /**
  101. * Filesystem instance
  102. *
  103. * @var \Magento\Framework\Filesystem
  104. * @since 100.1.0
  105. */
  106. protected $filesystem;
  107. /**
  108. * @var Import
  109. */
  110. private $import;
  111. /**
  112. * @var RateQueryFactory
  113. */
  114. private $rateQueryFactory;
  115. /**
  116. * Tablerate constructor.
  117. * @param \Magento\Framework\Model\ResourceModel\Db\Context $context
  118. * @param \Psr\Log\LoggerInterface $logger
  119. * @param \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig
  120. * @param \Magento\Store\Model\StoreManagerInterface $storeManager
  121. * @param \Magento\OfflineShipping\Model\Carrier\Tablerate $carrierTablerate
  122. * @param Filesystem $filesystem
  123. * @param RateQueryFactory $rateQueryFactory
  124. * @param Import $import
  125. * @param null $connectionName
  126. */
  127. public function __construct(
  128. \Magento\Framework\Model\ResourceModel\Db\Context $context,
  129. \Psr\Log\LoggerInterface $logger,
  130. \Magento\Framework\App\Config\ScopeConfigInterface $coreConfig,
  131. \Magento\Store\Model\StoreManagerInterface $storeManager,
  132. \Magento\OfflineShipping\Model\Carrier\Tablerate $carrierTablerate,
  133. \Magento\Framework\Filesystem $filesystem,
  134. Import $import,
  135. RateQueryFactory $rateQueryFactory,
  136. $connectionName = null
  137. ) {
  138. parent::__construct($context, $connectionName);
  139. $this->coreConfig = $coreConfig;
  140. $this->logger = $logger;
  141. $this->storeManager = $storeManager;
  142. $this->carrierTablerate = $carrierTablerate;
  143. $this->filesystem = $filesystem;
  144. $this->import = $import;
  145. $this->rateQueryFactory = $rateQueryFactory;
  146. }
  147. /**
  148. * Define main table and id field name
  149. *
  150. * @return void
  151. */
  152. protected function _construct()
  153. {
  154. $this->_init('shipping_tablerate', 'pk');
  155. }
  156. /**
  157. * Return table rate array or false by rate request
  158. *
  159. * @param \Magento\Quote\Model\Quote\Address\RateRequest $request
  160. * @return array|bool
  161. */
  162. public function getRate(\Magento\Quote\Model\Quote\Address\RateRequest $request)
  163. {
  164. $connection = $this->getConnection();
  165. $select = $connection->select()->from($this->getMainTable());
  166. /** @var RateQuery $rateQuery */
  167. $rateQuery = $this->rateQueryFactory->create(['request' => $request]);
  168. $rateQuery->prepareSelect($select);
  169. $bindings = $rateQuery->getBindings();
  170. $result = $connection->fetchRow($select, $bindings);
  171. // Normalize destination zip code
  172. if ($result && $result['dest_zip'] == '*') {
  173. $result['dest_zip'] = '';
  174. }
  175. return $result;
  176. }
  177. /**
  178. * @param array $condition
  179. * @return $this
  180. * @throws \Magento\Framework\Exception\LocalizedException
  181. */
  182. private function deleteByCondition(array $condition)
  183. {
  184. $connection = $this->getConnection();
  185. $connection->beginTransaction();
  186. $connection->delete($this->getMainTable(), $condition);
  187. $connection->commit();
  188. return $this;
  189. }
  190. /**
  191. * @param array $fields
  192. * @param array $values
  193. * @throws \Magento\Framework\Exception\LocalizedException
  194. * @return void
  195. */
  196. private function importData(array $fields, array $values)
  197. {
  198. $connection = $this->getConnection();
  199. $connection->beginTransaction();
  200. try {
  201. if (count($fields) && count($values)) {
  202. $this->getConnection()->insertArray($this->getMainTable(), $fields, $values);
  203. $this->_importedRows += count($values);
  204. }
  205. } catch (\Magento\Framework\Exception\LocalizedException $e) {
  206. $connection->rollBack();
  207. throw new \Magento\Framework\Exception\LocalizedException(__('Unable to import data'), $e);
  208. } catch (\Exception $e) {
  209. $connection->rollBack();
  210. $this->logger->critical($e);
  211. throw new \Magento\Framework\Exception\LocalizedException(
  212. __('Something went wrong while importing table rates.')
  213. );
  214. }
  215. $connection->commit();
  216. }
  217. /**
  218. * Upload table rate file and import data from it
  219. *
  220. * @param \Magento\Framework\DataObject $object
  221. * @throws \Magento\Framework\Exception\LocalizedException
  222. * @return \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
  223. * @todo: this method should be refactored as soon as updated design will be provided
  224. * @see https://wiki.corp.x.com/display/MCOMS/Magento+Filesystem+Decisions
  225. * @SuppressWarnings(PHPMD.CyclomaticComplexity)
  226. * @SuppressWarnings(PHPMD.NPathComplexity)
  227. */
  228. public function uploadAndImport(\Magento\Framework\DataObject $object)
  229. {
  230. /**
  231. * @var \Magento\Framework\App\Config\Value $object
  232. */
  233. if (empty($_FILES['groups']['tmp_name']['tablerate']['fields']['import']['value'])) {
  234. return $this;
  235. }
  236. $filePath = $_FILES['groups']['tmp_name']['tablerate']['fields']['import']['value'];
  237. $websiteId = $this->storeManager->getWebsite($object->getScopeId())->getId();
  238. $conditionName = $this->getConditionName($object);
  239. $file = $this->getCsvFile($filePath);
  240. try {
  241. // delete old data by website and condition name
  242. $condition = [
  243. 'website_id = ?' => $websiteId,
  244. 'condition_name = ?' => $conditionName,
  245. ];
  246. $this->deleteByCondition($condition);
  247. $columns = $this->import->getColumns();
  248. $conditionFullName = $this->_getConditionFullName($conditionName);
  249. foreach ($this->import->getData($file, $websiteId, $conditionName, $conditionFullName) as $bunch) {
  250. $this->importData($columns, $bunch);
  251. }
  252. } catch (\Exception $e) {
  253. $this->logger->critical($e);
  254. throw new \Magento\Framework\Exception\LocalizedException(
  255. __('Something went wrong while importing table rates.')
  256. );
  257. } finally {
  258. $file->close();
  259. }
  260. if ($this->import->hasErrors()) {
  261. $error = __(
  262. 'We couldn\'t import this file because of these errors: %1',
  263. implode(" \n", $this->import->getErrors())
  264. );
  265. throw new \Magento\Framework\Exception\LocalizedException($error);
  266. }
  267. }
  268. /**
  269. * @param \Magento\Framework\DataObject $object
  270. * @return mixed|string
  271. * @since 100.1.0
  272. */
  273. public function getConditionName(\Magento\Framework\DataObject $object)
  274. {
  275. if ($object->getData('groups/tablerate/fields/condition_name/inherit') == '1') {
  276. $conditionName = (string)$this->coreConfig->getValue('carriers/tablerate/condition_name', 'default');
  277. } else {
  278. $conditionName = $object->getData('groups/tablerate/fields/condition_name/value');
  279. }
  280. return $conditionName;
  281. }
  282. /**
  283. * @param string $filePath
  284. * @return \Magento\Framework\Filesystem\File\ReadInterface
  285. */
  286. private function getCsvFile($filePath)
  287. {
  288. $pathInfo = pathinfo($filePath);
  289. $dirName = isset($pathInfo['dirname']) ? $pathInfo['dirname'] : '';
  290. $fileName = isset($pathInfo['basename']) ? $pathInfo['basename'] : '';
  291. $directoryRead = $this->filesystem->getDirectoryReadByPath($dirName);
  292. return $directoryRead->openFile($fileName);
  293. }
  294. /**
  295. * Return import condition full name by condition name code
  296. *
  297. * @param string $conditionName
  298. * @return string
  299. */
  300. protected function _getConditionFullName($conditionName)
  301. {
  302. if (!isset($this->_conditionFullNames[$conditionName])) {
  303. $name = $this->carrierTablerate->getCode('condition_name_short', $conditionName);
  304. $this->_conditionFullNames[$conditionName] = $name;
  305. }
  306. return $this->_conditionFullNames[$conditionName];
  307. }
  308. /**
  309. * Save import data batch
  310. *
  311. * @param array $data
  312. * @return \Magento\OfflineShipping\Model\ResourceModel\Carrier\Tablerate
  313. */
  314. protected function _saveImportData(array $data)
  315. {
  316. if (!empty($data)) {
  317. $columns = [
  318. 'website_id',
  319. 'dest_country_id',
  320. 'dest_region_id',
  321. 'dest_zip',
  322. 'condition_name',
  323. 'condition_value',
  324. 'price',
  325. ];
  326. $this->getConnection()->insertArray($this->getMainTable(), $columns, $data);
  327. $this->_importedRows += count($data);
  328. }
  329. return $this;
  330. }
  331. }