From a92c9c426014545068d18ec8cf3d00e800e38143 Mon Sep 17 00:00:00 2001 From: Akolzin Dmitry Date: Tue, 20 Feb 2018 16:50:36 +0300 Subject: [PATCH] Job for upload specials price to CRM --- README.ru.md | 8 + .../controller/extension/module/retailcrm.php | 34 ++- .../en-gb/extension/module/retailcrm.php | 2 + .../ru-ru/extension/module/retailcrm.php | 2 + admin/model/extension/retailcrm/icml.php | 108 +++------- admin/model/extension/retailcrm/prices.php | 201 ++++++++++++++++++ .../model/extension/retailcrm/references.php | 83 +++++++- .../template/extension/module/retailcrm.tpl | 15 ++ .../template/extension/module/retailcrm.twig | 17 +- system/cron/prices.php | 4 + .../library/retailcrm/RetailcrmApiClient4.php | 37 ++++ .../library/retailcrm/RetailcrmApiClient5.php | 37 ++++ system/library/retailcrm/retailcrm.php | 74 +++++++ 13 files changed, 532 insertions(+), 90 deletions(-) create mode 100644 admin/model/extension/retailcrm/prices.php create mode 100644 system/cron/prices.php diff --git a/README.ru.md b/README.ru.md index 629d537..cf86498 100644 --- a/README.ru.md +++ b/README.ru.md @@ -55,6 +55,14 @@ cp -r opencart-module/* /path/to/site/root ``` http://youropencartsite.com/retailcrm.xml ``` +#### Настройка выгрузки акционных цен + +Для периодической выгрузки акционных цен в CRM в настройках модуля укажите тип цены, в который необходимо выгружать акционные цены +В крон добавьте следующую запись + +``` +0 0 * * * /usr/bin/php /path/to/opencart/system/cron/prices.php >> /path/to/opencart/system/storage/logs/cronjob_prices.log 2>&1 +``` #### Выгрузка существующих заказов и покупателей diff --git a/admin/controller/extension/module/retailcrm.php b/admin/controller/extension/module/retailcrm.php index 7620dd4..b55fd02 100644 --- a/admin/controller/extension/module/retailcrm.php +++ b/admin/controller/extension/module/retailcrm.php @@ -242,7 +242,9 @@ class ControllerExtensionModuleRetailcrm extends Controller 'text_customers_custom_fields', 'text_confirm_log', 'text_error_delivery', - 'retailcrm_missing_status' + 'retailcrm_missing_status', + 'special_price_settings', + 'special_price' ); $_data = &$data; @@ -278,6 +280,11 @@ class ControllerExtensionModuleRetailcrm extends Controller $_data['customFields'] = $this->model_extension_retailcrm_references ->getCustomFields(); } + + if ($apiVersion != 'v3') { + $_data['priceTypes'] = $this->model_extension_retailcrm_references + ->getPriceTypes(); + } } $config_data = array( @@ -507,7 +514,7 @@ class ControllerExtensionModuleRetailcrm extends Controller /** * Export single order * - * + * @return void */ public function exportOrder() { @@ -558,8 +565,8 @@ class ControllerExtensionModuleRetailcrm extends Controller * * @return void */ - public function export() { - + public function export() + { $this->load->model('customer/customer'); $customers = $this->model_customer_customer->getCustomers(); @@ -589,6 +596,25 @@ class ControllerExtensionModuleRetailcrm extends Controller fopen(DIR_SYSTEM . '/cron/export_done', "x"); } + /** + * Promotional price upload + * + * @return void + */ + public function prices() + { + $this->load->model('catalog/product'); + $products = $this->model_catalog_product->getProducts(); + + if (file_exists(DIR_APPLICATION . 'model/extension/retailcrm/custom/prices.php')) { + $this->load->model('extension/retailcrm/custom/prices'); + $this->model_extension_retailcrm_custom_prices->uploadPrices($products); + } else { + $this->load->model('extension/retailcrm/prices'); + $this->model_extension_retailcrm_prices->uploadPrices($products); + } + } + /** * Validate * diff --git a/admin/language/en-gb/extension/module/retailcrm.php b/admin/language/en-gb/extension/module/retailcrm.php index 1542a1c..0fe71fc 100644 --- a/admin/language/en-gb/extension/module/retailcrm.php +++ b/admin/language/en-gb/extension/module/retailcrm.php @@ -23,6 +23,8 @@ $_['retailcrm_apiversion'] = 'API Version'; $_['retailcrm_url'] = 'RetailCRM URL'; $_['retailcrm_apikey'] = 'RetailCRM API Key'; $_['collector_site_key'] = 'Site key'; +$_['special_price_settings'] = 'Settings specials'; +$_['special_price'] = 'The type of price at which the share price will be unloaded'; $_['text_success_export'] = 'Orders and customers successfully unloaded'; $_['text_success_export_order'] = 'Order successfully unloaded'; diff --git a/admin/language/ru-ru/extension/module/retailcrm.php b/admin/language/ru-ru/extension/module/retailcrm.php index 7311b02..42e8b36 100644 --- a/admin/language/ru-ru/extension/module/retailcrm.php +++ b/admin/language/ru-ru/extension/module/retailcrm.php @@ -23,6 +23,8 @@ $_['retailcrm_apiversion'] = 'Версия API'; $_['retailcrm_url'] = 'Адрес RetailCRM'; $_['retailcrm_apikey'] = 'Api ключ RetailCRM'; $_['collector_site_key'] = 'Ключ сайта'; +$_['special_price_settings'] = 'Настройка выгрузки акционной цены'; +$_['special_price'] = 'Тип цены, в который будет выгружаться цена по акции'; $_['text_success_export'] = 'Заказы и клиенты успешно выгружены'; $_['text_success_export_order'] = 'Заказ успешно выгружен'; diff --git a/admin/model/extension/retailcrm/icml.php b/admin/model/extension/retailcrm/icml.php index 5535e3a..d0afb5c 100644 --- a/admin/model/extension/retailcrm/icml.php +++ b/admin/model/extension/retailcrm/icml.php @@ -13,6 +13,17 @@ class ModelExtensionRetailcrmIcml extends Model private $options; private $optionValues; + /** + * Constructor + * + * @param Registry $registry + */ + public function __construct($registry) + { + parent::__construct($registry); + $this->load->library('retailcrm/retailcrm'); + } + public function generateICML() { $this->load->language('extension/module/retailcrm'); @@ -100,79 +111,17 @@ class ModelExtensionRetailcrmIcml extends Model $products = $this->model_catalog_product->getProducts(array()); foreach ($products as $product) { - // Формируем офферы отнсительно доступных опций - $options = $this->model_catalog_product->getProductOptions($product['product_id']); - $offerOptions = array('select', 'radio'); - $requiredOptions = array(); - $notRequiredOptions = array(); - // Оставляем опции связанные с вариациями товаров, сортируем по параметру обязательный или нет - foreach($options as $option) { - if(in_array($option['type'], $offerOptions)) { - if($option['required']) { - $requiredOptions[] = $option; - } else { - $notRequiredOptions[] = $option; - } - } - } - $offers = array(); - // Сначала совмещаем все обязательные опции - foreach($requiredOptions as $requiredOption) { - // Если первая итерация - if(empty($offers)) { - foreach($requiredOption['product_option_value'] as $optionValue) { - $offers[$requiredOption['product_option_id'].':'.$requiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( - 'price' => (float)$optionValue['price'], - 'qty' => $optionValue['quantity'] - ); - } - } else { - foreach($offers as $optionKey => $optionAttr) { - unset($offers[$optionKey]); // Работая в контексте обязательных опций не забываем удалять прошлые обязательные опции, т.к. они должны быть скомбинированы с другими обязательными опциями - foreach($requiredOption['product_option_value'] as $optionValue) { - $offers[$optionKey.'_'.$requiredOption['product_option_id'].':'.$requiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( - 'price' => $optionAttr['price'] + (float)$optionValue['price'], - 'qty' => ($optionAttr['qty'] > $optionValue['quantity']) ? - $optionValue['quantity'] : $optionAttr['qty'] - ); - } - } - } - } - // Совмещаем или добавляем необязательные опции, учитывая тот факт что обязательных опций может и не быть. - foreach($notRequiredOptions as $notRequiredOption) { - // Если обязательных опцией не оказалось и первая итерация - if(empty($offers)) { - $offers['0:0-0'] = 0; // В случае работы с необязательными опциями мы должны учитывать товарное предложение без опций, поэтому создадим "пустую" опцию - foreach($notRequiredOption['product_option_value'] as $optionValue) { - $offers[$notRequiredOption['product_option_id'].':'.$notRequiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( - 'price' => (float)$optionValue['price'], - 'qty' => $optionValue['quantity'] - ); - } - } else { - foreach($offers as $optionKey => $optionAttr) { - foreach($notRequiredOption['product_option_value'] as $optionValue) { - $offers[$optionKey.'_'.$notRequiredOption['product_option_id'].':'.$notRequiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( - 'price' => $optionAttr['price'] + (float)$optionValue['price'], - 'qty' => ($optionAttr['qty'] > $optionValue['quantity']) ? - $optionValue['quantity'] : $optionAttr['qty'] - ); - } - } - } - } - if(empty($offers)) { - $offers = array('0:0-0' => array('price' => '0', 'qty' => '0')); - } + $offers = $this->retailcrm->getOffers($product); - foreach($offers as $optionsString => $optionsValues) { + foreach ($offers as $optionsString => $optionsValues) { $optionsString = explode('_', $optionsString); $options = array(); + foreach($optionsString as $optionString) { $option = explode('-', $optionString); $optionIds = explode(':', $option[0]); - if($optionString != '0:0-0') { + + if ($optionString != '0:0-0') { $optionData = $this->getOptionData($optionIds[1], $option[1]); $options[$optionIds[0]] = array( 'name' => $optionData['optionName'], @@ -181,19 +130,23 @@ class ModelExtensionRetailcrmIcml extends Model ); } } + ksort($options); + $offerId = array(); + foreach($options as $optionKey => $optionData) { $offerId[] = $optionKey.'-'.$optionData['value_id']; } + $offerId = implode('_', $offerId); $e = $this->eOffers->appendChild($this->dd->createElement('offer')); - if(!empty($offerId)) { - $e->setAttribute('id', $product['product_id'].'#'.$offerId); + + if (!empty($offerId)) { + $e->setAttribute('id', $product['product_id'] . '#' . $offerId); $e->setAttribute('productId', $product['product_id']); $e->setAttribute('quantity', $optionsValues['qty']); - } - else { + } else { $e->setAttribute('id', $product['product_id']); $e->setAttribute('productId', $product['product_id']); $e->setAttribute('quantity', $product['quantity']); @@ -324,28 +277,23 @@ class ModelExtensionRetailcrmIcml extends Model $width, $height ); - - - return $this->model_tool_image->resize( - $image, - $this->config->get('config_image_product_width'), - $this->config->get('config_image_product_height') - ); } private function getOptionData($optionId, $optionValueId) { - if(!empty($this->options[$optionId])) { + if (!empty($this->options[$optionId])) { $option = $this->options[$optionId]; } else { $option = $this->model_catalog_option->getOption($optionId); $this->options[$optionId] = $option; } - if(!empty($this->optionValues[$optionValueId])) { + + if (!empty($this->optionValues[$optionValueId])) { $optionValue = $this->optionValues[$optionValueId]; } else { $optionValue = $this->model_catalog_option->getOptionValue($optionValueId); $this->optionValues[$optionValueId] = $optionValue; } + return array( 'optionName' => $option['name'], 'optionValue' => $optionValue['name'] diff --git a/admin/model/extension/retailcrm/prices.php b/admin/model/extension/retailcrm/prices.php new file mode 100644 index 0000000..f190861 --- /dev/null +++ b/admin/model/extension/retailcrm/prices.php @@ -0,0 +1,201 @@ +load->library('retailcrm/retailcrm'); + $this->load->model('catalog/option'); + $this->load->model('setting/setting'); + + $this->moduleTitle = $this->retailcrm->getModuleTitle(); + $this->settings = $this->model_setting_setting->getSetting($this->moduleTitle); + $this->retailcrmApiClient = $this->retailcrm->getApiClient(); + } + + /** + * Upload prices to CRM + * + * @param array $products + * + * @return void + */ + public function uploadPrices($products) + { + $prices = $this->getPrices($products); + + $pricesUpload = array_chunk($prices, 250); + + foreach ($pricesUpload as $priceUpload) { + $this->retailcrmApiClient->storePricesUpload($priceUpload); + } + } + + /** + * Get prices + * + * @param array $products + * + * @return array $prices + */ + protected function getPrices($products) + { + $prices = array(); + $site = $this->getSite(); + + if (!isset($this->settings[$this->moduleTitle . '_special']) + || $this->settings[$this->moduleTitle . '_apiversion'] == 'v3' + ) { + return; + } + + foreach ($products as $product) { + $specials = $this->model_catalog_product->getProductSpecials($product['product_id']); + + if (!$specials) { + continue; + } + + if (is_array($specials) && count($specials)) { + $productPrice = $this->getSpecialPrice($specials); + + if (!$productPrice) { + continue; + } + } + + $offers = $this->retailcrm->getOffers($product); + + foreach ($offers as $optionsString => $optionsValues) { + $optionsString = explode('_', $optionsString); + $options = array(); + + foreach($optionsString as $optionString) { + $option = explode('-', $optionString); + $optionIds = explode(':', $option[0]); + + if ($optionString != '0:0-0') { + $optionData = $this->getOptionData($optionIds[1], $option[1]); + $options[$optionIds[0]] = array( + 'name' => $optionData['optionName'], + 'value' => $optionData['optionValue'], + 'value_id' => $option[1] + ); + } + } + + ksort($options); + + $offerId = array(); + + foreach($options as $optionKey => $optionData) { + $offerId[] = $optionKey.'-'.$optionData['value_id']; + } + + $offerId = implode('_', $offerId); + + $prices[] = array( + 'externalId' => $offerId ? $product['product_id'] . '#' . $offerId : $product['product_id'], + 'site' => $site, + 'prices' => array( + array( + 'code' => $this->settings[$this->moduleTitle . '_special'], + 'price' => $productPrice + $optionsValues['price'] + ) + ) + ); + } + } + + return $prices; + } + + /** + * Get actual special + * + * @param array $specials + * + * @return float $productPrice + */ + private function getSpecialPrice($specials) + { + $date = date('Y-m-d'); + $always = '0000-00-00'; + $productPrice = 0; + + foreach ($specials as $special) { + if (($special['date_start'] == $always && $special['date_end'] == $always) + || ($special['date_start'] <= $date && $special['date_end'] >= $date) + ) { + if ((isset($priority) && $priority > $special['priority']) + || !isset($priority) + ) { + $productPrice = $special['price']; + $priority = $special['priority']; + } + } + } + + return $productPrice; + } + + /** + * Get data option + * + * @param int $optionId + * @param int $optionValueId + * + * @return array + */ + private function getOptionData($optionId, $optionValueId) { + if (!empty($this->options[$optionId])) { + $option = $this->options[$optionId]; + } else { + $option = $this->model_catalog_option->getOption($optionId); + $this->options[$optionId] = $option; + } + + if (!empty($this->optionValues[$optionValueId])) { + $optionValue = $this->optionValues[$optionValueId]; + } else { + $optionValue = $this->model_catalog_option->getOptionValue($optionValueId); + $this->optionValues[$optionValueId] = $optionValue; + } + + return array( + 'optionName' => $option['name'], + 'optionValue' => $optionValue['name'] + ); + } + + /** + * Get site + * + * @return mixed boolean | string + */ + private function getSite() + { + $response = $this->retailcrmApiClient->sitesList(); + + if ($response && $response->isSuccessful()) { + $sites = $response->sites; + $site = end($sites); + + return $site['code']; + } + + return false; + } +} diff --git a/admin/model/extension/retailcrm/references.php b/admin/model/extension/retailcrm/references.php index 9bf781d..a465030 100644 --- a/admin/model/extension/retailcrm/references.php +++ b/admin/model/extension/retailcrm/references.php @@ -1,9 +1,11 @@ settings = $this->model_setting_setting->getSetting($this->moduleTitle); $this->retailcrmApiClient = $this->retailcrm->getApiClient(); } - + /** + * Get opencart delivery methods + * + * @return array + */ public function getOpercartDeliveryTypes() { $this->opencartApiClient = $this->retailcrm->getOcApiClient($this->registry); @@ -25,6 +31,11 @@ class ModelExtensionRetailcrmReferences extends Model { return $this->opencartApiClient->request('retailcrm/getDeliveryTypes', array(), array()); } + /** + * Get all delivery types + * + * @return array + */ public function getDeliveryTypes() { $this->load->model('setting/store'); @@ -35,6 +46,11 @@ class ModelExtensionRetailcrmReferences extends Model { ); } + /** + * Get all statuses + * + * @return array + */ public function getOrderStatuses() { return array( @@ -43,6 +59,11 @@ class ModelExtensionRetailcrmReferences extends Model { ); } + /** + * Get all payment types + * + * @return array + */ public function getPaymentTypes() { return array( @@ -51,6 +72,11 @@ class ModelExtensionRetailcrmReferences extends Model { ); } + /** + * Get all custom fields + * + * @return array + */ public function getCustomFields() { return array( @@ -59,6 +85,11 @@ class ModelExtensionRetailcrmReferences extends Model { ); } + /** + * Get opencart order statuses + * + * @return array + */ public function getOpercartOrderStatuses() { $this->load->model('localisation/order_status'); @@ -67,6 +98,11 @@ class ModelExtensionRetailcrmReferences extends Model { ->getOrderStatuses(array()); } + /** + * Get opencart payment types + * + * @return array + */ public function getOpercartPaymentTypes() { $paymentTypes = array(); @@ -94,14 +130,24 @@ class ModelExtensionRetailcrmReferences extends Model { return $paymentTypes; } - + + /** + * Get opencart custom fields + * + * @return array + */ public function getOpencartCustomFields() { $this->load->model('customer/custom_field'); return $this->model_customer_custom_field->getCustomFields(); } - + + /** + * Get RetailCRM delivery types + * + * @return array + */ public function getApiDeliveryTypes() { $response = $this->retailcrmApiClient->deliveryTypesList(); @@ -109,6 +155,11 @@ class ModelExtensionRetailcrmReferences extends Model { return (!$response->isSuccessful()) ? array() : $response->deliveryTypes; } + /** + * Get RetailCRM order statuses + * + * @return array + */ public function getApiOrderStatuses() { $response = $this->retailcrmApiClient->statusesList(); @@ -116,13 +167,23 @@ class ModelExtensionRetailcrmReferences extends Model { return (!$response->isSuccessful()) ? array() : $response->statuses; } + /** + * Get RetailCRM payment types + * + * @return array + */ public function getApiPaymentTypes() { $response = $this->retailcrmApiClient->paymentTypesList(); return (!$response->isSuccessful()) ? array() : $response->paymentTypes; } - + + /** + * Get RetailCRM custom fields + * + * @return array + */ public function getApiCustomFields() { $customers = $this->retailcrmApiClient->customFieldsList(array('entity' => 'customer')); @@ -137,4 +198,16 @@ class ModelExtensionRetailcrmReferences extends Model { return array('customers' => $customFieldsCustomers, 'orders' => $customFieldsOrders); } + + /** + * Get RetailCRM price types + * + * @return array + */ + public function getPriceTypes() + { + $response = $this->retailcrmApiClient->priceTypesList(); + + return (!$response->isSuccessful()) ? array() : $response->priceTypes; + } } diff --git a/admin/view/template/extension/module/retailcrm.tpl b/admin/view/template/extension/module/retailcrm.tpl index f20f78f..18dc0bd 100644 --- a/admin/view/template/extension/module/retailcrm.tpl +++ b/admin/view/template/extension/module/retailcrm.tpl @@ -98,6 +98,21 @@ + +

+
+ + +
+
diff --git a/admin/view/template/extension/module/retailcrm.twig b/admin/view/template/extension/module/retailcrm.twig index 0d44af4..0413bf3 100644 --- a/admin/view/template/extension/module/retailcrm.twig +++ b/admin/view/template/extension/module/retailcrm.twig @@ -1,4 +1,4 @@ -{{ header }}{{ column_left }} +{{ header }} {{ column_left }}
+ {% if saved_settings.retailcrm_apiversion is defined and saved_settings.retailcrm_apiversion. != 'v3' %} +

{{ special_price_settings }}

+
+ + +
+ {% endif %}
diff --git a/system/cron/prices.php b/system/cron/prices.php new file mode 100644 index 0000000..9b6aa0f --- /dev/null +++ b/system/cron/prices.php @@ -0,0 +1,4 @@ +client->makeRequest( + '/store/prices/upload', + RetailcrmHttpClient::METHOD_POST, + array('prices' => json_encode($prices)) + ); + } + /** * Get delivery settings * @@ -1412,6 +1432,23 @@ class RetailcrmApiClient4 ); } + /** + * Returns price types list + * + * @throws \InvalidArgumentException + * @throws \RetailCrm\Exception\CurlException + * @throws \RetailCrm\Exception\InvalidJsonException + * + * @return ApiResponse + */ + public function priceTypesList() + { + return $this->client->makeRequest( + '/reference/price-types', + RetailcrmHttpClient::METHOD_GET + ); + } + /** * Get telephony settings * diff --git a/system/library/retailcrm/RetailcrmApiClient5.php b/system/library/retailcrm/RetailcrmApiClient5.php index 8ffd4e1..1ea4619 100644 --- a/system/library/retailcrm/RetailcrmApiClient5.php +++ b/system/library/retailcrm/RetailcrmApiClient5.php @@ -1504,6 +1504,26 @@ class RetailcrmApiClient5 ); } + /** + * Upload prices + * + * @param array $prices (default: array()) + * + * @throws \InvalidArgumentException + * @throws \RetailCrm\Exception\CurlException + * @throws \RetailCrm\Exception\InvalidJsonException + * + * @return ApiResponse + */ + public function storePricesUpload(array $prices = array()) + { + return $this->client->makeRequest( + '/store/prices/upload', + RetailcrmHttpClient::METHOD_POST, + array('prices' => json_encode($prices)) + ); + } + /** * Get delivery settings * @@ -2054,6 +2074,23 @@ class RetailcrmApiClient5 ); } + /** + * Returns price types list + * + * @throws \InvalidArgumentException + * @throws \RetailCrm\Exception\CurlException + * @throws \RetailCrm\Exception\InvalidJsonException + * + * @return ApiResponse + */ + public function priceTypesList() + { + return $this->client->makeRequest( + '/reference/price-types', + RetailcrmHttpClient::METHOD_GET + ); + } + /** * Get telephony settings * diff --git a/system/library/retailcrm/retailcrm.php b/system/library/retailcrm/retailcrm.php index 1740251..f6ae4d5 100644 --- a/system/library/retailcrm/retailcrm.php +++ b/system/library/retailcrm/retailcrm.php @@ -89,4 +89,78 @@ class Retailcrm { return $token; } + + public function getOffers($product) + { + // Формируем офферы отнсительно доступных опций + $options = $this->model_catalog_product->getProductOptions($product['product_id']); + $offerOptions = array('select', 'radio'); + $requiredOptions = array(); + $notRequiredOptions = array(); + // Оставляем опции связанные с вариациями товаров, сортируем по параметру обязательный или нет + foreach($options as $option) { + if(in_array($option['type'], $offerOptions)) { + if($option['required']) { + $requiredOptions[] = $option; + } else { + $notRequiredOptions[] = $option; + } + } + } + + $offers = array(); + // Сначала совмещаем все обязательные опции + foreach($requiredOptions as $requiredOption) { + // Если первая итерация + if(empty($offers)) { + foreach($requiredOption['product_option_value'] as $optionValue) { + $offers[$requiredOption['product_option_id'].':'.$requiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( + 'price' => (float)$optionValue['price'], + 'qty' => $optionValue['quantity'] + ); + } + } else { + foreach($offers as $optionKey => $optionAttr) { + unset($offers[$optionKey]); // Работая в контексте обязательных опций не забываем удалять прошлые обязательные опции, т.к. они должны быть скомбинированы с другими обязательными опциями + foreach($requiredOption['product_option_value'] as $optionValue) { + $offers[$optionKey.'_'.$requiredOption['product_option_id'].':'.$requiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( + 'price' => $optionAttr['price'] + (float)$optionValue['price'], + 'qty' => ($optionAttr['qty'] > $optionValue['quantity']) ? + $optionValue['quantity'] : $optionAttr['qty'] + ); + } + } + } + } + + // Совмещаем или добавляем необязательные опции, учитывая тот факт что обязательных опций может и не быть. + foreach($notRequiredOptions as $notRequiredOption) { + // Если обязательных опцией не оказалось и первая итерация + if(empty($offers)) { + $offers['0:0-0'] = 0; // В случае работы с необязательными опциями мы должны учитывать товарное предложение без опций, поэтому создадим "пустую" опцию + foreach($notRequiredOption['product_option_value'] as $optionValue) { + $offers[$notRequiredOption['product_option_id'].':'.$notRequiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( + 'price' => (float)$optionValue['price'], + 'qty' => $optionValue['quantity'] + ); + } + } else { + foreach($offers as $optionKey => $optionAttr) { + foreach($notRequiredOption['product_option_value'] as $optionValue) { + $offers[$optionKey.'_'.$notRequiredOption['product_option_id'].':'.$notRequiredOption['option_id'].'-'.$optionValue['option_value_id']] = array( + 'price' => $optionAttr['price'] + (float)$optionValue['price'], + 'qty' => ($optionAttr['qty'] > $optionValue['quantity']) ? + $optionValue['quantity'] : $optionAttr['qty'] + ); + } + } + } + } + + if(empty($offers)) { + $offers = array('0:0-0' => array('price' => '0', 'qty' => '0')); + } + + return $offers; + } }