diff --git a/RELEASE_NOTES.txt b/RELEASE_NOTES.txt index 291d8b802a..b09f515d9a 100644 --- a/RELEASE_NOTES.txt +++ b/RELEASE_NOTES.txt @@ -1,3 +1,205 @@ +==== 1.6.1.0 ==== + +=== Major Highlights === +Added two-step password reset flow + +=== Improvements === +XmlConnect package release v22.0 +Added support for using Shift-Click to select a range of grid rows when clicking check boxes +Added ability to register during checkout when using PayPal Express +Updated PayflowLink HSS user interface in checkout +"Add to Wishlist", "Add to Compare" were added on the Product Details Page for configurable, bundled and downloadable products +Updated webservices API + +=== Changes === +TheFind integration was removed +Google Optimizer was removed (it will be supported as a core extension) +Improved how discounts are applied to sub products + +=== Fixes === +Fixed Incorrect tax summary if taxes applied has different priorities +Fixed Shipping method extension after disable module returns error +Fixed Approved Review of a product is not displayed in the catalog +Fixed Special price is not considered for bundle dynamic products in "Your Customization" block +Fixed Incorrect behavior of changing quantity for composite products in Wishlist tab of customer's page in backend +Fixed MAP: Exc. Tax. amount is not shown on product's page when set apply map = 'On Gesture' +Fixed There has been an error processing your request is displayed by searching in Manage Checkout Terms and Conditions +Fixed Button Reorder is absent when Order contains Configurable product +Fixed Unable to place order if several tax rates with different priorities applied to bundle product +Fixed Incorrect Tracking Request to shipping carrier (in case with FedEx) +Fixed 'Click for price' not clickable after pressing 'Review' link in category +Fixed XML Connect: Impossible to submit application +Fixed MAP: with IE8 JS error appears in catalog +Fixed Error message appears after successful checkout with PayPal Express Checkout on IE9 +Fixed Incorrect invoice document creation behaviour for bundle dynamic product +Fixed XML Connect: Impossible to save template +Fixed Wrong focusing in WYSIWYG on IE9, impossible to save data after inserting content +Fixed Unable to set zero price for item in bundle product +Fixed Incorrect tax summary if several tax rates with different priorities applied to bundle product +Fixed Unable to create order from backend if Tax Calculation Method Based On = Unit Price/Row Total +Fixed Incorrect Tracking Request to shipping carrier (in case with FedEx) +Fixed Incorrect tax summary for partial documents if Tax Calculation Method Based On = Row Total/Unit Price +Fixed Problems with partial authorization process (Authorize.net) during Admin Order creation +Fixed Incorrect HTML markup of Installer page in IE7 and IE9 +Fixed "Apply Rules" button works incorrect in some cases +Fixed Incorrect behavior of changing quantity for composite products in Wishlist tab of customer's page in backend +Fixed Incorrect tax summary if product prices exclude tax +Fixed Redirect to blank page, when click Add Wishlist on the Bundle product page +Fixed Reviews not showing in category list page +Fixed QTY is wrong calculated for Bundle, Virtual, Simple, Configurable, Downloadable products after editing them in wishlist +Fixed Customer is redirected to shopping cart page instead to the "Ship to Multiple Addresses" page after login or register as a new customer +Fixed Configurable product with selected option is deleted from wishlist after updating another configurable product +Fixed After redirecting to product using tag 'click for price' doesn't work +Fixed "Notify for Quantity Below" doesn't work +Fixed Pending Reviews RSS doesn't show reviews, created for products, that are assigned not to Main Website +Fixed Authorize Direct Post: no successful notification about place order if do 'Edit' or 'Reorder' +Fixed Impossible to place Order with Payfowlink when Payment Action = Sale +Fixed Poll shows incorrect percentage +Fixed Message 'Cannot specify wishlist item.' is displayed by configuring products in wishlist in back-end +Fixed Billing Agreements: Order status is not updated if do actions from sandbox PayPal account +Fixed Additional authorization transaction is not displayed in Order's Transactions tab +Fixed Unable to update already created role by Admin +Fixed CDN Secure URL is used instead of non-secure +Fixed Attributes name disappeared during sorting +Fixed Typo in app/design/adminhtml/default/default/template/paypal/system/config/payflowlink/info.phtml +Fixed Report of Reviews shows all customer reviews when select certain user +Fixed Address Line Formats Incorrectly on PDF Invoices +Fixed Can't search transactions by order_id in manager.paypal.com +Fixed Wishlist Index Controller does unneeded logging of exception +Fixed Incorrect SQL generated in review product collection resource +Fixed Added shipping address rates collecting schedule for shipping method when shipping information step bypassed +Fixed Customer marked as guest in "Newsletter Subscribers" grid, after subscribing to newsletters in Google Checkout +Fixed Error is displayed by unchecking "Same As Billing Address" by creating order in admin +Fixed Tooltip doesn't appear if you put mouse pointer in to disabled package type field (in case with USPS Domestic) +Fixed Unable to select custom theme with underscores in name when creating a widget instance +Fixed When Redirect to Shopping Cart = No, choosing Remove item from shopping cart sidebar while editing an item leads to endless redirect loop +Fixed Saved CC form is not displayed, when there are no other available payment methods except Saved CC +Fixed It's impossible to create Catalog Price Rule +Fixed Fatal error on Multiple Addresses Checkout +Fixed Package Types that not available for current Shipping Method displayed in Create Packages pop-up +Fixed Stock item product getter not correspond to product setter +- added method getProduct() +Fixed Admin user interface: mistakes in labels names +Fixed Typo in Mage_Eav_Model_Resource_Entity_Attribute_Option model +Fixed Billing Agreement error +Fixed Payflow Link UI Changes +Fixed 'Website Payments Pro' impossible to place order during onepage checkout +Fixed Table rates works incorrect with asterisk +Fixed Typo in Category Resource Model +Fixed 3D secure with Saved CC works incorrectly +- removed unrelated message which told validation failed (even if it has actually succeeded) when trying to re-validate a card +Fixed Bug in Role Permission +Fixed Orders placed via Google Checkout were not created on the Magento side +Fixed When using direct Export, the _super_product_sku and _super_product_option on the configurable product does not match +Fixed Composite product price in grid is displayed incorrectly with some currencies due to JS regexp problem +Fixed No error message on Payflow link iframe +Fixed Flex uploader elements overlaps hovering menu items in backend +Fixed Unable to upload images in Magento installed on local server +Fixed Configurable Products - Use Default (attribute name) does not work correctly on IE9 +Fixed Capture failed when Verification Authorization Amount is set to Zero +Fixed Attribute is sorted like a string even when Input Validation for Store Owner is an Integer Number +Fixed Void and Cancel Order doesn't work (PayflowLink HSS) +Fixed Grand Total (Excl Tax) with negative value displays in the printed Credit Memo +Fixed Resource model of Media module is wrongly declared +Fixed Constraint violation with core_cache_tag table +Fixed Misprint in \downloader\lib\Mage\Connect\Validator.php +Fixed XMLRPC API attribute status changing +Fixed Typo in Mage_Rss_Block_Catalog_Category::_toHtml() method +Fixed Incorrect tax summary for partial credit memos/invoices +Fixed SSL is not used for links in email templates when admin area is configured to use HTTPS +Fixed Incorrect style on product page +Fixed Error is displayed by editing product or by creating product on back-end in IE8 +Fixed Catalog price rules for composite products changes +Fixed Moving modules to the correct place +Fixed Wishlist shows items per store scope, not website +Fixed Products in Wishlist disappears, when Store View is changed +Fixed Wrong Comments History in notification of order creation/cancellation +Fixed In AJAX popup fields "From" and "To" have behavior as mandatory fields +Fixed Filter by Allow Countries not working for Customer Address Form in the Backend +Fixed Product price lower than 0 (after catalog price rule applying) +Fixed Google Checkout throws error if Zip Range is used for Tax Rate +- changed part of XML request to Google responsible for postal codes +- made changes to correctly fetch tax rules for postal code ranges +Fixed Website config object is not being cached +Fixed Select groups in grid view doesn't work under IE7 +Fixed No products name in Popular tags report file .csv +Fixed Qty Increments should work when it was defined in the default scope configuration +Fixed Products in catalog displays as "out of stock" +Fixed "Get help for this page" in each tab under System->Configuration links to the same help page +Fixed Unable to translate submenu +Fixed Redirect to main page of front-end during deleting Product Tax Class which is used in Tax Rule +Fixed Redirect to base URL should consider full request URI string +Fixed Incorrect transparency of PNG image in indexed non-alpha mode +Fixed Problem of generation URL between different domains +Fixed There are no server side validation of first character of Attribute Code (it should be letter) +Fixed Edit Order without creating new one functionality saves invalid changes in non-default customer address attributes +Fixed Frontend: If second customer logs in and does not select the "Remember Me" then the previous long-term cookie does not removes +Fixed Admin can Reorder order with status On Hold +Fixed Frontend: After new customer registration with "Remember Me" and pressing "Logout" the long-term cookie session doesn't apply if in configuration on backend ""Remember Me" Default Value" - No +Fixed Tax not displaying on PayPal side for Express orders +Fixed Wishlist: Not configured grouped product has unneeded link "Show Details" +Fixed Removed the ability to work with customise admin url through the parameter base_url +Fixed Roles not displaying selected resources +Fixed Unable to use Import when compiler is enabled +Fixed Missing Translation Capability in Transactional Email Variable +Fixed productConfigure is undefined error is occurred during creation Order in Backend in IE8 browser +Fixed Zend Full Page Cache. Lifetime of the cookie is not equal to specified on "Cookie Lifetime" field +Fixed Incorrect price values for Bundle Product +Fixed Link does not pass validation if ends with .html +Fixed Incorrect Customs Value in Create Packages in case when price value contains decimals +Fixed Incorrect reports with updated_at filter +Fixed paypal_payment_transaction_clean job takes credentials form default config instead of website for Payflow Link +Fixed Wrong schedule time setup for paypal_payment_transaction_clean job for Payflow Link +Fixed Backend Error message "SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry 'qwe' for key 2" appears after saving new Email Template with existing name +Fixed Rule Conditions logic +Fixed Error appears after Customer Group saving with name length more than 32 +Fixed Category product index run time +Fixed Sidebar cart is missing composite product options on category page +Fixed Missed validation for space character at the begin of unique fields +- improved validation of Attribute Set Name and validation of unique fields in Mage_Core_Model_Resource_Db_Abstract class +Fixed Ability to input uppercase, space, specials symbols in Order Status Code +Fixed Filter by Allow Countries not working for Customer Address Form in the Backend +Fixed No ability to create Shipping Label with "plus-four codes" Zip Code (in case with USPS Domestic) +Fixed Changing language twice -> Error 404 +Fixed The sort order in products page doesn't work +Fixed When a grouped product with configured price=0 is added to the wishlist and shared, adding the product to the cart leads to 404 error +Fixed Media Saves Incorrect Cached Config +- added options that disallow saving cache +Fixed Bundle Product items shows randomly instead of according to option +Fixed Ajax loader does not appears after click on Verify Card on Payflow Link +Fixed "Google Checkout - Carrier" in Magento backend as shipping method rather than the actual shipping method chosen +Fixed JavaScript error appears in checkout because of PSC after press 'Proceed to Checkout' +Fixed Admin user interface: mistakes in labels names +Fixed Problems with grid sorting on edit customer backend page +Fixed It is possible to change the price of the Bundle product from fixed to the dynamic at my store +Fixed Set special price via Catalog Product API is not working +Fixed Price is wrong calculated for bundle product with a zero price for its items on product details page +- subitem price calculation were fixed +Fixed Layout issue appears in IE9 on the grids (example Customers) +Fixed Impossible to press 'Continue' button to place in onepage +Fixed "Add to Wishlist", "Add to Compare" aren't presented for Configurable, bundle, downloadable, simple products on Product Details Page +Fixed Register during checkout with PayPal Express Checkout +Fixed Extension Packager does not read recursive directory if include expression use file mask +Fixed Newsletter Subscription Confirmation Message +Fixed Discount is wrong calculated for Shopping Cart Price Rules when some of them created with Coupon and another without Coupon +Fixed Subtotal (Incl.Tax) on invoices must not include tax applied to shipping amount +- shippingTax amount were excluded from subTotal value +Fixed "Maximum shipping amount allowed to refund" message shows amount excl. tax if Display Shipping Amount set to Including Tax +- adjusted function to include tax into allowed amount for shipping refund +Fixed Display Out of Stock Products must not be considered during admin order creation +Fixed Check box is not working correctly under "prices" of the Bundle products +Fixed CSS class missing +Fixed Frontend: JavaScript error appears if user registered on Checkout Page +Fixed Website config object is not being cached +- added functionality for memcache backend to split down data that is larger than slab size into chunks +Fixed Removed the ability to work with base_url +Fixed Custom design should be updated via import functionality +Fixed JS error during onepage checkout +Fixed Unable to translate "First name" and "Last name" fields on "Create an Account" page +Fixed After upgrading dashboard "Top 5 Search Terms" grid doesn't show search terms + + + ==== 1.6.1.0-rc1 ==== === Major Highlights === diff --git a/app/Mage.php b/app/Mage.php index 240f870896..4d50995d90 100644 --- a/app/Mage.php +++ b/app/Mage.php @@ -154,8 +154,8 @@ public static function getVersionInfo() 'minor' => '6', 'revision' => '1', 'patch' => '0', - 'stability' => 'rc', - 'number' => '1', + 'stability' => '', + 'number' => '', ); } diff --git a/app/code/core/Mage/Api/Helper/Data.php b/app/code/core/Mage/Api/Helper/Data.php index ddf394c8a6..0ededf8474 100644 --- a/app/code/core/Mage/Api/Helper/Data.php +++ b/app/code/core/Mage/Api/Helper/Data.php @@ -49,28 +49,71 @@ public function isComplianceWSI() * @param Object $obj - Link to Object * @return Object */ - public function wsiArrayUnpacker(&$obj){ - if(is_object($obj)){ + public function wsiArrayUnpacker(&$obj) + { + if (is_object($obj)) { $modifiedKeys = $this->clearWsiFootprints($obj); - foreach( $obj as $key => $value ){ - if(is_object($value)){ + foreach ($obj as $key => $value) { + if (is_object($value)) { $this->wsiArrayUnpacker($value); } - if(is_array($value)){ - foreach($value as &$val){ - if(is_object($val)){ + if (is_array($value)) { + foreach ($value as &$val) { + if (is_object($val)) { $this->wsiArrayUnpacker($val); } } } } + + foreach ($modifiedKeys as $arrKey) { + $this->associativeArrayUnpack($obj->$arrKey); + } } + } - foreach($modifiedKeys as $arrKey){ - $this->assocativArrayUnpacker($obj->$arrKey); + /** + * Go thru an object parameters and unpak associative object to array. + * + * @param Object $obj - Link to Object + * @return Object + */ + public function v2AssociativeArrayUnpacker(&$obj) + { + if (is_object($obj) + && property_exists($obj, 'key') + && property_exists($obj, 'value') + ) { + if (count(array_keys(get_object_vars($obj))) == 2) { + $obj = array($obj->key => $obj->value); + return true; + } + } elseif (is_array($obj)) { + $arr = array(); + $needReplacement = true; + foreach ($obj as $key => &$value) { + $isAssoc = $this->v2AssociativeArrayUnpacker($value); + if ($isAssoc) { + foreach ($value as $aKey => $aVal) { + $arr[$aKey] = $aVal; + } + } else { + $needReplacement = false; + } + } + if ($needReplacement) { + $obj = $arr; + } + } elseif (is_object($obj)) { + $objectKeys = array_keys(get_object_vars($obj)); + + foreach ($objectKeys as $key) { + $this->v2AssociativeArrayUnpacker($obj->$key); + } } + return false; } /** @@ -78,34 +121,34 @@ public function wsiArrayUnpacker(&$obj){ * * @param Mixed $mixed A link to variable that may contain associative array. */ - public function assocativArrayUnpacker(&$mixed){ - - if(is_array($mixed)){ - $tmpArr = array(); - foreach($mixed as $key => $value){ - if(is_object($value)){ - $value = get_object_vars($value); - if(count($value) == 2 && isset($value['key']) && isset($value['value'])){ - $tmpArr[$value['key']] = $value['value']; - } + public function associativeArrayUnpack(&$mixed) + { + if (is_array($mixed)) { + $tmpArr = array(); + foreach ($mixed as $key => $value) { + if (is_object($value)) { + $value = get_object_vars($value); + if (count($value) == 2 && isset($value['key']) && isset($value['value'])) { + $tmpArr[$value['key']] = $value['value']; } } - if(count($tmpArr)){ - $mixed = $tmpArr; - } } + if (count($tmpArr)) { + $mixed = $tmpArr; + } + } - if(is_object($mixed)){ - $numOfVals = count(get_object_vars($mixed)); - if($numOfVals == 2 && isset($mixed->key) && isset($mixed->value)){ - $mixed = get_object_vars($mixed); - /* - * Processing an associative arrays. - * $mixed->key = '2'; $mixed->value = '3'; turns to array(2 => '3'); - */ - $mixed = array($mixed['key'] => $mixed['value']); - } + if (is_object($mixed)) { + $numOfVals = count(get_object_vars($mixed)); + if ($numOfVals == 2 && isset($mixed->key) && isset($mixed->value)) { + $mixed = get_object_vars($mixed); + /* + * Processing an associative arrays. + * $mixed->key = '2'; $mixed->value = '3'; turns to array(2 => '3'); + */ + $mixed = array($mixed['key'] => $mixed['value']); } + } } /** @@ -114,13 +157,14 @@ public function assocativArrayUnpacker(&$mixed){ * @param Object $obj - Link to Object * @return Object */ - public function clearWsiFootprints(&$obj){ + public function clearWsiFootprints(&$obj) + { $modifiedKeys = array(); $objectKeys = array_keys(get_object_vars($obj)); - foreach( $objectKeys as $key ){ - if(is_object($obj->$key) && isset($obj->$key->complexObjectArray) ){ + foreach ($objectKeys as $key) { + if (is_object($obj->$key) && isset($obj->$key->complexObjectArray)) { $obj->$key = $obj->$key->complexObjectArray; $modifiedKeys[] = $key; } @@ -131,28 +175,29 @@ public function clearWsiFootprints(&$obj){ /** * For the WSI, generates an response object. * - * @param Object $arr - Link to Object - * @return Object + * @param mixed $mixed - Link to Object + * @return mixed */ - public function wsiArrayPacker($mixed){ - if(is_array($mixed)){ + public function wsiArrayPacker($mixed) + { + if (is_array($mixed)) { $arrKeys = array_keys($mixed); $isDigit = false; $isString = false; - foreach($arrKeys as $key){ - if(is_int($key)){ + foreach ($arrKeys as $key) { + if (is_int($key)) { $isDigit = true; break; } } - if($isDigit){ + if ($isDigit) { $mixed = $this->packArrayToObjec($mixed); } else { - $mixed = (object)$mixed; + $mixed = (object) $mixed; } } - if(is_object($mixed) && isset($mixed->complexObjectArray)){ - foreach($mixed->complexObjectArray as $k => $v){ + if (is_object($mixed) && isset($mixed->complexObjectArray)) { + foreach ($mixed->complexObjectArray as $k => $v) { $mixed->complexObjectArray[$k] = $this->wsiArrayPacker($v); } } @@ -165,9 +210,30 @@ public function wsiArrayPacker($mixed){ * @param Array $arr - Link to Object * @return Object */ - public function packArrayToObjec(Array $arr){ + public function packArrayToObjec(Array $arr) + { $obj = new stdClass(); $obj->complexObjectArray = $arr; return $obj; } + + /** + * Convert objects and arrays to array recursively + * + * @param array|object $data + * @return void + */ + public function toArray(&$data) + { + if (is_object($data)) { + $data = get_object_vars($data); + } + if (is_array($data)) { + foreach ($data as &$value) { + if (is_array($value) or is_object($value)) { + $this->toArray($value); + } + } + } + } } // Class Mage_Api_Helper_Data End diff --git a/app/code/core/Mage/Api/Model/Acl.php b/app/code/core/Mage/Api/Model/Acl.php index 9f78a51ae4..1628ac3626 100644 --- a/app/code/core/Mage/Api/Model/Acl.php +++ b/app/code/core/Mage/Api/Model/Acl.php @@ -46,6 +46,17 @@ class Mage_Api_Model_Acl extends Zend_Acl */ const ROLE_TYPE_USER = 'U'; + /** + * User types for store access + * G - Guest customer (anonymous) + * C - Authenticated customer + * A - Authenticated admin user + * + */ + const USER_TYPE_GUEST = 'G'; + const USER_TYPE_CUSTOMER = 'C'; + const USER_TYPE_ADMIN = 'A'; + /** * Permission level to deny access * diff --git a/app/code/core/Mage/Api/Model/Server.php b/app/code/core/Mage/Api/Model/Server.php index 19e0c966d4..2f77af4e7d 100644 --- a/app/code/core/Mage/Api/Model/Server.php +++ b/app/code/core/Mage/Api/Model/Server.php @@ -33,6 +33,13 @@ */ class Mage_Api_Model_Server { + + /** + * Api Name by Adapter + * @var string + */ + protected $_api = ""; + /** * Web service adapter * @@ -44,6 +51,7 @@ public function init(Mage_Api_Controller_Action $controller, $adapter='default', { $adapters = Mage::getSingleton('api/config')->getActiveAdapters(); $handlers = Mage::getSingleton('api/config')->getHandlers(); + $this->_api = $adapter; if (isset($adapters[$adapter])) { $adapterModel = Mage::getModel((string) $adapters[$adapter]->model); /* @var $adapterModel Mage_Api_Model_Server_Adapter_Interface */ @@ -76,6 +84,15 @@ public function run() $this->getAdapter()->run(); } + /** + * Get Api name by Adapter + * @return string + */ + public function getApiName() + { + return $this->_api; + } + /** * Retrieve web service adapter * diff --git a/app/code/core/Mage/Api/etc/wsi.xml b/app/code/core/Mage/Api/etc/wsi.xml index ded00510c1..0db28ab286 100644 --- a/app/code/core/Mage/Api/etc/wsi.xml +++ b/app/code/core/Mage/Api/etc/wsi.xml @@ -19,6 +19,17 @@ + + + + + + + + + + + diff --git a/app/code/core/Mage/Bundle/Block/Catalog/Product/Price.php b/app/code/core/Mage/Bundle/Block/Catalog/Product/Price.php index c0c30dd0c8..571e6a8817 100644 --- a/app/code/core/Mage/Bundle/Block/Catalog/Product/Price.php +++ b/app/code/core/Mage/Bundle/Block/Catalog/Product/Price.php @@ -74,6 +74,7 @@ protected function _toHtml() if ($this->getMAPTemplate() && Mage::helper('catalog')->canApplyMsrp($product) && $product->getPriceType() != Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC ) { + $hiddenPriceHtml = parent::_toHtml(); if (Mage::helper('catalog')->isShowPriceOnGesture($product)) { $this->setWithoutPrice(true); } @@ -84,8 +85,9 @@ protected function _toHtml() $html = $this->getLayout() ->createBlock('catalog/product_price') ->setTemplate($this->getMAPTemplate()) - ->setRealPriceHtml($realPriceHtml) - ->setIdSuffix('_clone') + ->setRealPriceHtml($hiddenPriceHtml) + ->setPriceElementIdPrefix('bundle-price-') + ->setIdSuffix($this->getIdSuffix()) ->setProduct($product) ->toHtml(); diff --git a/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php b/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php index f5106d17c7..d8e5daef87 100644 --- a/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php +++ b/app/code/core/Mage/Bundle/Block/Catalog/Product/View/Type/Bundle.php @@ -81,6 +81,7 @@ public function getJsonConfig() $selected = array(); $currentProduct = $this->getProduct(); $coreHelper = Mage::helper('core'); + $bundlePriceModel = Mage::getModel('bundle/product_price'); if ($preconfiguredFlag = $currentProduct->hasPreconfiguredValues()) { $preconfiguredValues = $currentProduct->getPreconfiguredValues(); @@ -120,14 +121,8 @@ public function getJsonConfig() ); } - $itemPrice = $_selection->getFinalPrice(); - if ($_selection->getSelectionPriceValue() != 0) { - if ($_selection->getSelectionPriceType()) { // percent - $itemPrice = $currentProduct->getFinalPrice() * $_selection->getSelectionPriceValue() / 100; - } else { // fixed - $itemPrice = $_selection->getSelectionPriceValue(); - } - } + $itemPrice = $bundlePriceModel->getSelectionFinalTotalPrice($currentProduct, $_selection, + $currentProduct->getQty(), $_selection->getQty()); $canApplyMAP = false; diff --git a/app/code/core/Mage/Bundle/Model/Product/Price.php b/app/code/core/Mage/Bundle/Model/Product/Price.php index bb98efb8a0..748e771322 100644 --- a/app/code/core/Mage/Bundle/Model/Product/Price.php +++ b/app/code/core/Mage/Bundle/Model/Product/Price.php @@ -372,12 +372,7 @@ public function getSelectionPrice($bundleProduct, $selectionProduct, $selectionQ if ($selectionProduct->getSelectionPriceType()) { // percent return $bundleProduct->getPrice() * ($selectionProduct->getSelectionPriceValue() / 100) * $selectionQty; } else { // fixed - if ((float)$selectionProduct->getSelectionPriceValue()) { - return $selectionProduct->getSelectionPriceValue() * $selectionQty; - } else { - // Use product price, in case when item price set to 0 - return $selectionProduct->getFinalPrice($selectionQty); - } + return $selectionProduct->getSelectionPriceValue() * $selectionQty; } } } diff --git a/app/code/core/Mage/Catalog/Model/Api/Resource.php b/app/code/core/Mage/Catalog/Model/Api/Resource.php index 9cd461db80..bfe7e715ff 100644 --- a/app/code/core/Mage/Catalog/Model/Api/Resource.php +++ b/app/code/core/Mage/Catalog/Model/Api/Resource.php @@ -53,6 +53,12 @@ class Mage_Catalog_Model_Api_Resource extends Mage_Api_Model_Resource_Abstract */ protected $_storeIdSessionField = 'store_id'; + /** + * Name of resource model in ACL list + * @var string + */ + protected $_resourceAttributeAclName = 'catalog/category/attributes/field_'; + /** * Check is attribute allowed * @@ -62,6 +68,13 @@ class Mage_Catalog_Model_Api_Resource extends Mage_Api_Model_Resource_Abstract */ protected function _isAllowedAttribute($attribute, $attributes = null) { + + if (Mage::getSingleton('api/server')->getApiName() == 'rest') { + if (!$this->_checkAttributeAcl($attribute)) { + return false; + } + } + if (is_array($attributes) && !( in_array($attribute->getAttributeCode(), $attributes) || in_array($attribute->getAttributeId(), $attributes))) { @@ -106,6 +119,9 @@ protected function _getStoreId($store = null) protected function _getProduct($productId, $store = null, $identifierType = null) { $product = Mage::helper('catalog/product')->getProduct($productId, $this->_getStoreId($store), $identifierType); + if (is_null($product->getId())) { + $this->_fault('product_not_exists'); + } return $product; } diff --git a/app/code/core/Mage/Catalog/Model/Category/Api.php b/app/code/core/Mage/Catalog/Model/Category/Api.php index 091e6e3a59..3b51f812f4 100644 --- a/app/code/core/Mage/Catalog/Model/Category/Api.php +++ b/app/code/core/Mage/Catalog/Model/Category/Api.php @@ -39,10 +39,11 @@ public function __construct() } /** - * Retrive level of categories for category/store view/website + * Retrieve level of categories for category/store view/website * - * @param string|int $website - * @param string|int $store + * @param string|int|null $website + * @param string|int|null $store + * @param int|null $categoryId * @return array */ public function level($website = null, $store = null, $categoryId = null) @@ -240,39 +241,57 @@ public function info($categoryId, $store = null, $attributes = null) * * @param int $parentId * @param array $categoryData + * @param int|null|string $store * @return int */ public function create($parentId, $categoryData, $store = null) { $parent_category = $this->_initCategory($parentId, $store); + + /** @var $category Mage_Catalog_Model_Category */ $category = Mage::getModel('catalog/category') ->setStoreId($this->_getStoreId($store)); - $category->addData(array('path'=>implode('/',$parent_category->getPathIds()))); - - $category ->setAttributeSetId($category->getDefaultAttributeSetId()); - /* @var $category Mage_Catalog_Model_Category */ + $category->addData(array('path'=>implode('/', $parent_category->getPathIds()))); + $category->setAttributeSetId($category->getDefaultAttributeSetId()); + $useConfig = array(); foreach ($category->getAttributes() as $attribute) { if ($this->_isAllowedAttribute($attribute) - && isset($categoryData[$attribute->getAttributeCode()])) { - $category->setData( - $attribute->getAttributeCode(), - $categoryData[$attribute->getAttributeCode()] - ); + && isset($categoryData[$attribute->getAttributeCode()]) + ) { + // check whether value is 'use_config' + $attrCode = $attribute->getAttributeCode(); + $categoryDataValue = $categoryData[$attrCode]; + if ('use_config' === $categoryDataValue || + (is_array($categoryDataValue) && + count($categoryDataValue) == 1 && + 'use_config' === $categoryDataValue[0]) + ) { + $useConfig[] = $attrCode; + $category->setData($attrCode, null); + } else { + $category->setData($attrCode, $categoryDataValue); + } } } $category->setParentId($parent_category->getId()); + /** + * Proceed with $useConfig set into category model for processing through validation + */ + if (count($useConfig) > 0) { + $category->setData("use_post_data_config", $useConfig); + } + try { $validate = $category->validate(); if ($validate !== true) { foreach ($validate as $code => $error) { if ($error === true) { Mage::throwException(Mage::helper('catalog')->__('Attribute "%s" is required.', $code)); - } - else { + } else { Mage::throwException($error); } } @@ -283,6 +302,9 @@ public function create($parentId, $categoryData, $store = null) catch (Mage_Core_Exception $e) { $this->_fault('data_invalid', $e->getMessage()); } + catch (Exception $e) { + $this->_fault('data_invalid', $e->getMessage()); + } return $category->getId(); } diff --git a/app/code/core/Mage/Catalog/Model/Category/Attribute/Api.php b/app/code/core/Mage/Catalog/Model/Category/Attribute/Api.php index 1977a6e045..04058ec294 100644 --- a/app/code/core/Mage/Catalog/Model/Category/Attribute/Api.php +++ b/app/code/core/Mage/Catalog/Model/Category/Attribute/Api.php @@ -72,7 +72,7 @@ public function items() return $result; } - /** + /** * Retrieve category attribute options * * @param int|string $attributeId @@ -92,7 +92,7 @@ public function options($attributeId, $store = null) $result = array(); if ($attribute->usesSource()) { - foreach ($attribute->getSource()->getAllOptions() as $optionId=>$optionValue) { + foreach ($attribute->getSource()->getAllOptions(false) as $optionId=>$optionValue) { if (is_array($optionValue)) { $result[] = $optionValue; } else { diff --git a/app/code/core/Mage/Catalog/Model/Product/Api.php b/app/code/core/Mage/Catalog/Model/Product/Api.php index 293c3ddcc3..b23698f02f 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Api.php +++ b/app/code/core/Mage/Catalog/Model/Product/Api.php @@ -39,6 +39,40 @@ class Mage_Catalog_Model_Product_Api extends Mage_Catalog_Model_Api_Resource 'type' => 'type_id' ); + protected $_defaultProductAttributeList = array( + 'type_id', + 'category_ids', + 'website_ids', + 'name', + 'description', + 'short_description', + 'sku', + 'weight', + 'status', + 'url_key', + 'url_path', + 'visibility', + 'has_options', + 'gift_message_available', + 'price', + 'special_price', + 'special_from_date', + 'special_to_date', + 'tax_class_id', + 'tier_price', + 'meta_title', + 'meta_keyword', + 'meta_description', + 'custom_design', + 'custom_layout_update', + 'options_container', + 'image_label', + 'small_image_label', + 'thumbnail_label', + 'created_at', + 'updated_at' + ); + public function __construct() { $this->_storeIdSessionField = 'product_store_id'; @@ -102,9 +136,6 @@ public function info($productId, $store = null, $attributes = null, $identifierT { $product = $this->_getProduct($productId, $store, $identifierType); - if (!$product->getId()) { - $this->_fault('not_exists'); - } $result = array( // Basic product data 'product_id' => $product->getId(), @@ -141,6 +172,9 @@ public function create($type, $set, $sku, $productData, $store = null) $this->_fault('data_invalid'); } + $this->_checkProductTypeExists($type); + $this->_checkProductAttributeSet($set); + /** @var $product Mage_Catalog_Model_Product */ $product = Mage::getModel('catalog/product'); $product->setStoreId($this->_getStoreId($store)) @@ -148,20 +182,6 @@ public function create($type, $set, $sku, $productData, $store = null) ->setTypeId($type) ->setSku($sku); - if (isset($productData['website_ids']) && is_array($productData['website_ids'])) { - $product->setWebsiteIds($productData['website_ids']); - } - - foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { - if ($this->_isAllowedAttribute($attribute) - && isset($productData[$attribute->getAttributeCode()])) { - $product->setData( - $attribute->getAttributeCode(), - $productData[$attribute->getAttributeCode()] - ); - } - } - $this->_prepareDataForSave($product, $productData); try { @@ -200,27 +220,8 @@ public function update($productId, $productData, $store = null, $identifierType { $product = $this->_getProduct($productId, $store, $identifierType); - if (!$product->getId()) { - $this->_fault('not_exists'); - } - - if (isset($productData['website_ids']) && is_array($productData['website_ids'])) { - $product->setWebsiteIds($productData['website_ids']); - } - - foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { - if ($this->_isAllowedAttribute($attribute) - && array_key_exists($attribute->getAttributeCode(), $productData)) { - $product->setData( - $attribute->getAttributeCode(), - $productData[$attribute->getAttributeCode()] - ); - } - } - $this->_prepareDataForSave($product, $productData); - try { /** * @todo implement full validation process with errors returning which are ignoring now @@ -254,8 +255,33 @@ public function update($productId, $productData, $store = null, $identifierType * @param array $productData * @return object */ - protected function _prepareDataForSave ($product, $productData) + protected function _prepareDataForSave($product, $productData) { + if (isset($productData['website_ids']) && is_array($productData['website_ids'])) { + $product->setWebsiteIds($productData['website_ids']); + } + + foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { + if ($this->_isAllowedAttribute($attribute)) { + if (isset($productData[$attribute->getAttributeCode()])) { + $product->setData( + $attribute->getAttributeCode(), + $productData[$attribute->getAttributeCode()] + ); + } elseif (isset($productData['additional_attributes']['single_data'][$attribute->getAttributeCode()])) { + $product->setData( + $attribute->getAttributeCode(), + $productData['additional_attributes']['single_data'][$attribute->getAttributeCode()] + ); + } elseif (isset($productData['additional_attributes']['multi_data'][$attribute->getAttributeCode()])) { + $product->setData( + $attribute->getAttributeCode(), + $productData['additional_attributes']['multi_data'][$attribute->getAttributeCode()] + ); + } + } + } + if (isset($productData['categories']) && is_array($productData['categories'])) { $product->setCategoryIds($productData['categories']); } @@ -329,10 +355,6 @@ public function delete($productId, $identifierType = null) { $product = $this->_getProduct($productId, null, $identifierType); - if (!$product->getId()) { - $this->_fault('not_exists'); - } - try { $product->delete(); } catch (Mage_Core_Exception $e) { @@ -341,4 +363,82 @@ public function delete($productId, $identifierType = null) return true; } + + /** + * Get list of additional attributes which are not in default create/update list + * + * @param $productType + * @param $attributeSetId + * @return array + */ + public function getAdditionalAttributes($productType, $attributeSetId) + { + $this->_checkProductTypeExists($productType); + $this->_checkProductAttributeSet($attributeSetId); + + /** @var $product Mage_Catalog_Model_Product */ + $productAttributes = Mage::getModel('catalog/product') + ->setAttributeSetId($attributeSetId) + ->setTypeId($productType) + ->getTypeInstance(false) + ->getEditableAttributes(); + + $result = array(); + foreach ($productAttributes as $attribute) { + /* @var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */ + if ($attribute->isInSet($attributeSetId) && $this->_isAllowedAttribute($attribute) + && !in_array($attribute->getAttributeCode(), $this->_defaultProductAttributeList)) { + + if ($attribute->isScopeGlobal()) { + $scope = 'global'; + } elseif ($attribute->isScopeWebsite()) { + $scope = 'website'; + } else { + $scope = 'store'; + } + + $result[] = array( + 'attribute_id' => $attribute->getId(), + 'code' => $attribute->getAttributeCode(), + 'type' => $attribute->getFrontendInput(), + 'required' => $attribute->getIsRequired(), + 'scope' => $scope + ); + } + } + + return $result; + } + + /** + * Check if product type exists + * + * @param $productType + * @throw Mage_Api_Exception + * @return void + */ + protected function _checkProductTypeExists($productType) + { + if (!in_array($productType, array_keys(Mage::getModel('catalog/product_type')->getOptionArray()))) { + $this->_fault('product_type_not_exists'); + } + } + + /** + * Check if attributeSet is exits and in catalog_product entity group type + * + * @param $attributeSetId + * @throw Mage_Api_Exception + * @return void + */ + protected function _checkProductAttributeSet($attributeSetId) + { + $attributeSet = Mage::getModel('eav/entity_attribute_set')->load($attributeSetId); + if (is_null($attributeSet->getId())) { + $this->_fault('product_attribute_set_not_exists'); + } + if (Mage::getModel('catalog/product')->getResource()->getTypeId() != $attributeSet->getEntityTypeId()) { + $this->_fault('product_attribute_set_not_valid'); + } + } } // Class Mage_Catalog_Model_Product_Api End diff --git a/app/code/core/Mage/Catalog/Model/Product/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Api/V2.php index 2b417e2fc0..3395546044 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Api/V2.php +++ b/app/code/core/Mage/Catalog/Model/Product/Api/V2.php @@ -105,10 +105,6 @@ public function info($productId, $store = null, $attributes = null, $identifierT { $product = $this->_getProduct($productId, $store, $identifierType); - if (!$product->getId()) { - $this->_fault('not_exists'); - } - $result = array( // Basic product data 'product_id' => $product->getId(), 'sku' => $product->getSku(), @@ -170,6 +166,9 @@ public function create($type, $set, $sku, $productData, $store = null) $this->_fault('data_invalid'); } + $this->_checkProductTypeExists($type); + $this->_checkProductAttributeSet($set); + /** @var $product Mage_Catalog_Model_Product */ $product = Mage::getModel('catalog/product'); $product->setStoreId($this->_getStoreId($store)) @@ -177,29 +176,6 @@ public function create($type, $set, $sku, $productData, $store = null) ->setTypeId($type) ->setSku($sku); - if (property_exists($productData, 'website_ids') && is_array($productData->website_ids)) { - $product->setWebsiteIds($productData->website_ids); - } - - if (property_exists($productData, 'additional_attributes')) { - foreach ($productData->additional_attributes as $_attribute) { - $_attrCode = $_attribute->key; - $productData->$_attrCode = $_attribute->value; - } - unset($productData->additional_attributes); - } - - foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { - $_attrCode = $attribute->getAttributeCode(); - if ($this->_isAllowedAttribute($attribute) - && isset($productData->$_attrCode)) { - $product->setData( - $attribute->getAttributeCode(), - $productData->$_attrCode - ); - } - } - $this->_prepareDataForSave($product, $productData); try { @@ -238,33 +214,6 @@ public function update($productId, $productData, $store = null, $identifierType { $product = $this->_getProduct($productId, $store, $identifierType); - if (!$product->getId()) { - $this->_fault('not_exists'); - } - - if (property_exists($productData, 'website_ids') && is_array($productData->website_ids)) { - $product->setWebsiteIds($productData->website_ids); - } - - if (property_exists($productData, 'additional_attributes')) { - foreach ($productData->additional_attributes as $_attribute) { - $_attrCode = $_attribute->key; - $productData->$_attrCode = $_attribute->value; - } - unset($productData->additional_attributes); - } - - foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { - $_attrCode = $attribute->getAttributeCode(); - if ($this->_isAllowedAttribute($attribute) - && property_exists($productData, $_attrCode)) { - $product->setData( - $attribute->getAttributeCode(), - $productData->$_attrCode - ); - } - } - $this->_prepareDataForSave($product, $productData); try { @@ -302,6 +251,36 @@ public function update($productId, $productData, $store = null, $identifierType */ protected function _prepareDataForSave ($product, $productData) { + if (property_exists($productData, 'website_ids') && is_array($productData->website_ids)) { + $product->setWebsiteIds($productData->website_ids); + } + + if (property_exists($productData, 'additional_attributes')) { + if (property_exists($productData->additional_attributes, 'single_data')) { + foreach ($productData->additional_attributes->single_data as $_attribute) { + $_attrCode = $_attribute->key; + $productData->$_attrCode = $_attribute->value; + } + } + if (property_exists($productData->additional_attributes, 'multi_data')) { + foreach ($productData->additional_attributes->multi_data as $_attribute) { + $_attrCode = $_attribute->key; + $productData->$_attrCode = $_attribute->value; + } + } + unset($productData->additional_attributes); + } + + foreach ($product->getTypeInstance(true)->getEditableAttributes($product) as $attribute) { + $_attrCode = $attribute->getAttributeCode(); + if ($this->_isAllowedAttribute($attribute) && (isset($productData->$_attrCode))) { + $product->setData( + $attribute->getAttributeCode(), + $productData->$_attrCode + ); + } + } + if (property_exists($productData, 'categories') && is_array($productData->categories)) { $product->setCategoryIds($productData->categories); } diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Api.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Api.php index c01c66a31a..0357dc3d3f 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Api.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Api.php @@ -33,12 +33,23 @@ */ class Mage_Catalog_Model_Product_Attribute_Api extends Mage_Catalog_Model_Api_Resource { + /** + * Product entity type id + * + * @var int + */ + protected $_entityTypeId; + + /** + * Constructor. Initializes default values. + */ public function __construct() { $this->_storeIdSessionField = 'product_store_id'; $this->_ignoredAttributeCodes[] = 'type_id'; $this->_ignoredAttributeTypes[] = 'gallery'; $this->_ignoredAttributeTypes[] = 'media_image'; + $this->_entityTypeId = Mage::getModel('eav/entity')->setType('catalog_product')->getTypeId(); } /** @@ -56,8 +67,8 @@ public function items($setId) foreach ($attributes as $attribute) { /* @var $attribute Mage_Catalog_Model_Resource_Eav_Attribute */ - if ( (!$attribute->getId() || $attribute->isInSet($setId)) - && $this->_isAllowedAttribute($attribute)) { + if ((!$attribute->getId() || $attribute->isInSet($setId)) + && $this->_isAllowedAttribute($attribute)) { if (!$attribute->getId() || $attribute->isScopeGlobal()) { $scope = 'global'; @@ -69,10 +80,10 @@ public function items($setId) $result[] = array( 'attribute_id' => $attribute->getId(), - 'code' => $attribute->getAttributeCode(), - 'type' => $attribute->getFrontendInput(), - 'required' => $attribute->getIsRequired(), - 'scope' => $scope + 'code' => $attribute->getAttributeCode(), + 'type' => $attribute->getFrontendInput(), + 'required' => $attribute->getIsRequired(), + 'scope' => $scope ); } } @@ -91,10 +102,9 @@ public function options($attributeId, $store = null) { $storeId = $this->_getStoreId($store); $attribute = Mage::getModel('catalog/product') - ->setStoreId($storeId) - ->getResource() - ->getAttribute($attributeId) - ->setStoreId($storeId); + ->setStoreId($storeId) + ->getResource() + ->getAttribute($attributeId); /* @var $attribute Mage_Catalog_Model_Entity_Attribute */ if (!$attribute) { @@ -102,7 +112,7 @@ public function options($attributeId, $store = null) } $options = array(); if ($attribute->usesSource()) { - foreach ($attribute->getSource()->getAllOptions() as $optionId=>$optionValue) { + foreach ($attribute->getSource()->getAllOptions() as $optionId => $optionValue) { if (is_array($optionValue)) { $options[] = $optionValue; } else { @@ -113,6 +123,404 @@ public function options($attributeId, $store = null) } } } + return $options; } + + /** + * Retrieve list of possible attribute types + * + * @return array + */ + public function types() + { + return Mage::getModel('catalog/product_attribute_source_inputtype')->toOptionArray(); + } + + /** + * Create new product attribute + * + * @param array $data input data + * @return integer + */ + public function create($data) + { + /** @var $model Mage_Catalog_Model_Resource_Eav_Attribute */ + $model = Mage::getModel('catalog/resource_eav_attribute'); + /** @var $helper Mage_Catalog_Helper_Product */ + $helper = Mage::helper('catalog/product'); + + if (empty($data['attribute_code']) || !is_array($data['frontend_label'])) { + $this->_fault('invalid_parameters'); + } + + //validate attribute_code + if (!preg_match('/^[a-z][a-z_0-9]{0,254}$/', $data['attribute_code'])) { + $this->_fault('invalid_code'); + } + + //validate frontend_input + $allowedTypes = array(); + foreach ($this->types() as $type) { + $allowedTypes[] = $type['value']; + } + if (!in_array($data['frontend_input'], $allowedTypes)) { + $this->_fault('invalid_frontend_input'); + } + + $data['source_model'] = $helper->getAttributeSourceModelByInputType($data['frontend_input']); + $data['backend_model'] = $helper->getAttributeBackendModelByInputType($data['frontend_input']); + if (is_null($model->getIsUserDefined()) || $model->getIsUserDefined() != 0) { + $data['backend_type'] = $model->getBackendTypeByInput($data['frontend_input']); + } + + $this->_prepareDataForSave($data); + + $model->addData($data); + $model->setEntityTypeId($this->_entityTypeId); + $model->setIsUserDefined(1); + + try { + $model->save(); + // clear translation cache because attribute labels are stored in translation + Mage::app()->cleanCache(array(Mage_Core_Model_Translate::CACHE_TAG)); + } catch (Exception $e) { + $this->_fault('unable_to_save', $e->getMessage()); + } + + return (int) $model->getId(); + } + + /** + * Update product attribute + * + * @param string|integer $attribute attribute code or ID + * @param array $data + * @return boolean + */ + public function update($attribute, $data) + { + $model = $this->_getAttribute($attribute); + + if ($model->getEntityTypeId() != $this->_entityTypeId) { + $this->_fault('can_not_edit'); + } + + $data['attribute_code'] = $model->getAttributeCode(); + $data['is_user_defined'] = $model->getIsUserDefined(); + $data['frontend_input'] = $model->getFrontendInput(); + + $this->_prepareDataForSave($data); + + $model->addData($data); + try { + $model->save(); + // clear translation cache because attribute labels are stored in translation + Mage::app()->cleanCache(array(Mage_Core_Model_Translate::CACHE_TAG)); + return true; + } catch (Exception $e) { + $this->_fault('unable_to_save', $e->getMessage()); + } + } + + /** + * Remove attribute + * + * @param integer|string $attribute attribute ID or code + * @return boolean + */ + public function remove($attribute) + { + $model = $this->_getAttribute($attribute); + + if ($model->getEntityTypeId() != $this->_entityTypeId) { + $this->_fault('can_not_delete'); + } + + try { + $model->delete(); + return true; + } catch (Exception $e) { + $this->_fault('can_not_delete', $e->getMessage()); + } + } + + /** + * Get full information about attribute with list of options + * + * @param integer|string $attribute attribute ID or code + * @return array + */ + public function info($attribute) + { + $model = $this->_getAttribute($attribute); + + if ($model->isScopeGlobal()) { + $scope = 'global'; + } elseif ($model->isScopeWebsite()) { + $scope = 'website'; + } else { + $scope = 'store'; + } + + $frontendLabels = array( + array( + 'store_id' => 0, + 'label' => $model->getFrontendLabel() + ) + ); + foreach ($model->getStoreLabels() as $store_id => $label) { + $frontendLabels[] = array( + 'store_id' => $store_id, + 'label' => $label + ); + } + + $result = array( + 'attribute_id' => $model->getId(), + 'attribute_code' => $model->getAttributeCode(), + 'frontend_input' => $model->getFrontendInput(), + 'default_value' => $model->getDefaultValue(), + 'is_unique' => $model->getIsUnique(), + 'is_required' => $model->getIsRequired(), + 'apply_to' => $model->getApplyTo(), + 'is_configurable' => $model->getIsConfigurable(), + 'is_searchable' => $model->getIsSearchable(), + 'is_visible_in_advanced_search' => $model->getIsVisibleInAdvancedSearch(), + 'is_comparable' => $model->getIsComparable(), + 'is_used_for_promo_rules' => $model->getIsUsedForPromoRules(), + 'is_visible_on_front' => $model->getIsVisibleOnFront(), + 'used_in_product_listing' => $model->getUsedInProductListing(), + 'frontend_label' => $frontendLabels + ); + if ($model->getFrontendInput() != 'price') { + $result['scope'] = $scope; + } + + // set additional fields to different types + switch ($model->getFrontendInput()) { + case 'text': + $result['additional_fields'] = array( + 'frontend_class' => $model->getFrontendClass(), + 'is_html_allowed_on_front' => $model->getIsHtmlAllowedOnFront(), + 'used_for_sort_by' => $model->getUsedForSortBy() + ); + break; + case 'textarea': + $result['additional_fields'] = array( + 'is_wysiwyg_enabled' => $model->getIsWysiwygEnabled(), + 'is_html_allowed_on_front' => $model->getIsHtmlAllowedOnFront(), + ); + break; + case 'date': + case 'boolean': + $result['additional_fields'] = array( + 'used_for_sort_by' => $model->getUsedForSortBy() + ); + break; + case 'multiselect': + $result['additional_fields'] = array( + 'is_filterable' => $model->getIsFilterable(), + 'is_filterable_in_search' => $model->getIsFilterableInSearch(), + 'position' => $model->getPosition() + ); + break; + case 'select': + case 'price': + $result['additional_fields'] = array( + 'is_filterable' => $model->getIsFilterable(), + 'is_filterable_in_search' => $model->getIsFilterableInSearch(), + 'position' => $model->getPosition(), + 'used_for_sort_by' => $model->getUsedForSortBy() + ); + break; + default: + $result['additional_fields'] = array(); + break; + } + + // set options + $options = $this->options($model->getId()); + // remove empty first element + if ($model->getFrontendInput() != 'boolean') { + array_shift($options); + } + + if (count($options) > 0) { + $result['options'] = $options; + } + + return $result; + } + + /** + * Add option to select or multiselect attribute + * + * @param integer|string $attribute attribute ID or code + * @param array $data + * @return bool + */ + public function addOption($attribute, $data) + { + $model = $this->_getAttribute($attribute); + + if (!$model->usesSource()) { + $this->_fault('invalid_frontend_input'); + } + + /** @var $helperCatalog Mage_Catalog_Helper_Data */ + $helperCatalog = Mage::helper('catalog'); + + $optionLabels = array(); + foreach ($data['label'] as $label) { + $storeId = $label['store_id']; + $labelText = $helperCatalog->stripTags($label['value']); + if (is_array($storeId)) { + foreach ($storeId as $multiStoreId) { + $optionLabels[$multiStoreId] = $labelText; + } + } else { + $optionLabels[$storeId] = $labelText; + } + } + // data in the following format is accepted by the model + // it simulates parameters of the request made to + // Mage_Adminhtml_Catalog_Product_AttributeController::saveAction() + $modelData = array( + 'option' => array( + 'value' => array( + 'option_1' => $optionLabels + ), + 'order' => array( + 'option_1' => (int) $data['order'] + ) + ) + ); + if ($data['is_default']) { + $modelData['default'][] = 'option_1'; + } + + $model->addData($modelData); + try { + $model->save(); + } catch (Exception $e) { + $this->_fault('unable_to_add_option', $e->getMessage()); + } + + return true; + } + + /** + * Remove option from select or multiselect attribute + * + * @param integer|string $attribute attribute ID or code + * @param integer $optionId option to remove ID + * @return bool + */ + public function removeOption($attribute, $optionId) + { + $model = $this->_getAttribute($attribute); + + if (!$model->usesSource()) { + $this->_fault('invalid_frontend_input'); + } + + // data in the following format is accepted by the model + // it simulates parameters of the request made to + // Mage_Adminhtml_Catalog_Product_AttributeController::saveAction() + $modelData = array( + 'option' => array( + 'value' => array( + $optionId => array() + ), + 'delete' => array( + $optionId => '1' + ) + ) + ); + $model->addData($modelData); + try { + $model->save(); + } catch (Exception $e) { + $this->_fault('unable_to_remove_option', $e->getMessage()); + } + + return true; + } + + /** + * Prepare request input data for saving + * + * @param array $data input data + * @return void + */ + protected function _prepareDataForSave(&$data) + { + /** @var $helperCatalog Mage_Catalog_Helper_Data */ + $helperCatalog = Mage::helper('catalog'); + + if ($data['scope'] == 'global') { + $data['is_global'] = Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL; + } else if ($data['scope'] == 'website') { + $data['is_global'] = Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_WEBSITE; + } else { + $data['is_global'] = Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_STORE; + } + if (!isset($data['is_configurable'])) { + $data['is_configurable'] = 0; + } + if (!isset($data['is_filterable'])) { + $data['is_filterable'] = 0; + } + if (!isset($data['is_filterable_in_search'])) { + $data['is_filterable_in_search'] = 0; + } + if (!isset($data['apply_to'])) { + $data['apply_to'] = array(); + } + // set frontend labels array with store_id as keys + if (isset($data['frontend_label']) && is_array($data['frontend_label'])) { + $labels = array(); + foreach ($data['frontend_label'] as $label) { + $storeId = $label['store_id']; + $labelText = $helperCatalog->stripTags($label['label']); + $labels[$storeId] = $labelText; + } + $data['frontend_label'] = $labels; + } + // set additional fields + if (isset($data['additional_fields']) && is_array($data['additional_fields'])) { + $data = array_merge($data, $data['additional_fields']); + unset($data['additional_fields']); + } + //default value + if (!empty($data['default_value'])) { + $data['default_value'] = $helperCatalog->stripTags($data['default_value']); + } + } + + /** + * Load model by attribute ID or code + * + * @param integer|string $attribute + * @return Mage_Catalog_Model_Resource_Eav_Attribute + */ + protected function _getAttribute($attribute) + { + $model = Mage::getResourceModel('catalog/eav_attribute') + ->setEntityTypeId($this->_entityTypeId); + + if (is_numeric($attribute)) { + $model->load(intval($attribute)); + } else { + $model->load($attribute, 'attribute_code'); + } + + if (!$model->getId()) { + $this->_fault('not_exists'); + } + + return $model; + } + } // Class Mage_Catalog_Model_Product_Attribute_Api End diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Api/V2.php index d577f73458..6a0dbba1c9 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Api/V2.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Api/V2.php @@ -33,4 +33,67 @@ */ class Mage_Catalog_Model_Product_Attribute_Api_V2 extends Mage_Catalog_Model_Product_Attribute_Api { + /** + * Create new product attribute + * + * @param array $data input data + * @return integer + */ + public function create($data) + { + $helper = Mage::helper('api'); + $helper->v2AssociativeArrayUnpacker($data); + Mage::helper('api')->toArray($data); + return parent::create($data); + } + + /** + * Update product attribute + * + * @param string|integer $attribute attribute code or ID + * @param array $data + * @return boolean + */ + public function update($attribute, $data) + { + $helper = Mage::helper('api'); + $helper->v2AssociativeArrayUnpacker($data); + Mage::helper('api')->toArray($data); + return parent::update($attribute, $data); + } + + /** + * Add option to select or multiselect attribute + * + * @param integer|string $attribute attribute ID or code + * @param array $data + * @return bool + */ + public function addOption($attribute, $data) + { + Mage::helper('api')->toArray($data); + return parent::addOption($attribute, $data); + } + + /** + * Get full information about attribute with list of options + * + * @param integer|string $attribute attribute ID or code + * @return array + */ + public function info($attribute) + { + $result = parent::info($attribute); + if (!empty($result['additional_fields'])){ + $keys = array_keys($result['additional_fields']); + foreach ($keys as $key ) { + $result['additional_fields'][] = array( + 'key' => $key, + 'value' => $result['additional_fields'][$key] + ); + unset($result['additional_fields'][$key]); + } + } + return $result; + } } diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Group.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Group.php new file mode 100644 index 0000000000..cf2dd5ef39 --- /dev/null +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Group.php @@ -0,0 +1,69 @@ +setAttributeGroupFilter($this->getId()); + foreach ($attributesCollection as $attribute) { + if (!$attribute->getIsUserDefined()) { + $result = true; + break; + } + } + return $result; + } + + /** + * Check if contains attributes used in the configurable products + * + * @return bool + */ + public function hasConfigurableAttributes() + { + $result = false; + /** @var $attributesCollection Mage_Catalog_Model_Resource_Product_Attribute_Collection */ + $attributesCollection = Mage::getResourceModel('catalog/product_attribute_collection'); + $attributesCollection->setAttributeGroupFilter($this->getId()); + foreach ($attributesCollection as $attribute) { + if ($attribute->getIsConfigurable()) { + $result = true; + break; + } + } + return $result; + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Set/Api.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Set/Api.php index c466fb2a71..c37e659a39 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Attribute/Set/Api.php +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Set/Api.php @@ -55,4 +55,233 @@ public function items() return $result; } + + /** + * Create new attribute set based on another set + * + * @param string $attributeSetName + * @param string $skeletonSetId + * @return integer + */ + public function create($attributeSetName, $skeletonSetId) + { + // check if set with requested $skeletonSetId exists + if (!Mage::getModel('eav/entity_attribute_set')->load($skeletonSetId)->getId()){ + $this->_fault('invalid_skeleton_set_id'); + } + // get catalog product entity type id + $entityTypeId = Mage::getModel('catalog/product')->getResource()->getTypeId(); + /** @var $attributeSet Mage_Eav_Model_Entity_Attribute_Set */ + $attributeSet = Mage::getModel('eav/entity_attribute_set') + ->setEntityTypeId($entityTypeId) + ->setAttributeSetName($attributeSetName); + try { + // check if name is valid + $attributeSet->validate(); + // copy parameters to new set from skeleton set + $attributeSet->save(); + $attributeSet->initFromSkeleton($skeletonSetId)->save(); + } catch (Mage_Eav_Exception $e) { + $this->_fault('invalid_data', $e->getMessage()); + } catch (Exception $e) { + $this->_fault('create_attribute_set_error', $e->getMessage()); + } + return (int)$attributeSet->getId(); + } + + /** + * Remove attribute set + * + * @param string $attributeSetId + * @param bool $forceProductsRemove + * @return bool + */ + public function remove($attributeSetId, $forceProductsRemove = false) + { + // if attribute set has related goods and $forceProductsRemove is not set throw exception + if (!$forceProductsRemove) { + /** @var $catalogProductsCollection Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Collection */ + $catalogProductsCollection = Mage::getModel('catalog/product')->getCollection() + ->addFieldToFilter('attribute_set_id', $attributeSetId); + if (count($catalogProductsCollection)) { + $this->_fault('attribute_set_has_related_products'); + } + } + $attributeSet = Mage::getModel('eav/entity_attribute_set')->load($attributeSetId); + // check if set with requested id exists + if (!$attributeSet->getId()){ + $this->_fault('invalid_attribute_set_id'); + } + try { + $attributeSet->delete(); + } catch (Exception $e) { + $this->_fault('remove_attribute_set_error', $e->getMessage()); + } + return true; + } + + /** + * Add attribute to attribute set + * + * @param string $attributeId + * @param string $attributeSetId + * @param string|null $attributeGroupId + * @param string $sortOrder + * @return bool + */ + public function attributeAdd($attributeId, $attributeSetId, $attributeGroupId = null, $sortOrder = '0') + { + // check if attribute with requested id exists + /** @var $attribute Mage_Eav_Model_Entity_Attribute */ + $attribute = Mage::getModel('eav/entity_attribute')->load($attributeId); + if (!$attribute->getId()) { + $this->_fault('invalid_attribute_id'); + } + // check if attribute set with requested id exists + /** @var $attributeSet Mage_Eav_Model_Entity_Attribute_Set */ + $attributeSet = Mage::getModel('eav/entity_attribute_set')->load($attributeSetId); + if (!$attributeSet->getId()) { + $this->_fault('invalid_attribute_set_id'); + } + if (!empty($attributeGroupId)) { + // check if attribute group with requested id exists + if (!Mage::getModel('eav/entity_attribute_group')->load($attributeGroupId)->getId()) { + $this->_fault('invalid_attribute_group_id'); + } + } else { + // define default attribute group id for current attribute set + $attributeGroupId = $attributeSet->getDefaultGroupId(); + } + $attribute->setAttributeSetId($attributeSet->getId())->loadEntityAttributeIdBySet(); + if ($attribute->getEntityAttributeId()) { + $this->_fault('attribute_is_already_in_set'); + } + try { + $attribute->setEntityTypeId($attributeSet->getEntityTypeId()) + ->setAttributeSetId($attributeSetId) + ->setAttributeGroupId($attributeGroupId) + ->setSortOrder($sortOrder) + ->save(); + } catch (Exception $e) { + $this->_fault('add_attribute_error', $e->getMessage()); + } + return true; + } + + /** + * Remove attribute from attribute set + * + * @param string $attributeId + * @param string $attributeSetId + * @return bool + */ + public function attributeRemove($attributeId, $attributeSetId) + { + // check if attribute with requested id exists + /** @var $attribute Mage_Eav_Model_Entity_Attribute */ + $attribute = Mage::getModel('eav/entity_attribute')->load($attributeId); + if (!$attribute->getId()) { + $this->_fault('invalid_attribute_id'); + } + // check if attribute set with requested id exists + /** @var $attributeSet Mage_Eav_Model_Entity_Attribute_Set */ + $attributeSet = Mage::getModel('eav/entity_attribute_set')->load($attributeSetId); + if (!$attributeSet->getId()) { + $this->_fault('invalid_attribute_set_id'); + } + // check if attribute is in set + $attribute->setAttributeSetId($attributeSet->getId())->loadEntityAttributeIdBySet(); + if (!$attribute->getEntityAttributeId()) { + $this->_fault('attribute_is_not_in_set'); + } + try { + // delete record from eav_entity_attribute + // using entity_attribute_id loaded by loadEntityAttributeIdBySet() + $attribute->deleteEntity(); + } catch (Exception $e) { + $this->_fault('remove_attribute_error', $e->getMessage()); + } + + return true; + } + + /** + * Create group within existing attribute set + * + * @param string|int $attributeSetId + * @param string $groupName + * @return int + */ + public function groupAdd($attributeSetId, $groupName) + { + /** @var $group Mage_Eav_Model_Entity_Attribute_Group */ + $group = Mage::getModel('eav/entity_attribute_group'); + $group->setAttributeSetId($attributeSetId) + ->setAttributeGroupName( + Mage::helper('catalog')->stripTags($groupName) + ); + if ($group->itemExists()) { + $this->_fault('group_already_exists'); + } + try { + $group->save(); + } catch (Exception $e) { + $this->_fault('group_add_error', $e->getMessage()); + } + return (int)$group->getId(); + } + + /** + * Rename existing group + * + * @param string|int $groupId + * @param string $groupName + * @return boolean + */ + public function groupRename($groupId, $groupName) + { + $model = Mage::getModel('eav/entity_attribute_group')->load($groupId); + + if (!$model->getAttributeGroupName()) { + $this->_fault('invalid_attribute_group_id'); + } + + $model->setAttributeGroupName( + Mage::helper('catalog')->stripTags($groupName) + ); + try { + $model->save(); + } catch (Exception $e) { + $this->_fault('group_rename_error', $e->getMessage()); + } + return true; + } + + /** + * Remove group from existing attribute set + * + * @param string|int $attributeGroupId + * @return bool + */ + public function groupRemove($attributeGroupId) + { + /** @var $group Mage_Catalog_Model_Product_Attribute_Group */ + $group = Mage::getModel('catalog/product_attribute_group')->load($attributeGroupId); + if (!$group->getId()) { + $this->_fault('invalid_attribute_group_id'); + } + if ($group->hasConfigurableAttributes()) { + $this->_fault('group_has_configurable_attributes'); + } + if ($group->hasSystemAttributes()) { + $this->_fault('group_has_system_attributes'); + } + try { + $group->delete(); + } catch (Exception $e) { + $this->_fault('group_remove_error', $e->getMessage()); + } + return true; + } + } // Class Mage_Catalog_Model_Product_Attribute_Set_Api End diff --git a/app/code/core/Mage/Catalog/Model/Product/Attribute/Source/Inputtype.php b/app/code/core/Mage/Catalog/Model/Product/Attribute/Source/Inputtype.php new file mode 100644 index 0000000000..ca058436ac --- /dev/null +++ b/app/code/core/Mage/Catalog/Model/Product/Attribute/Source/Inputtype.php @@ -0,0 +1,77 @@ + + */class Mage_Catalog_Model_Product_Attribute_Source_Inputtype extends Mage_Eav_Model_Adminhtml_System_Config_Source_Inputtype +{ + /** + * Get product input types as option array + * + * @return array + */ + public function toOptionArray() + { + $inputTypes = array( + array( + 'value' => 'price', + 'label' => Mage::helper('catalog')->__('Price') + ), + array( + 'value' => 'media_image', + 'label' => Mage::helper('catalog')->__('Media Image') + ) + ); + + $response = new Varien_Object(); + $response->setTypes(array()); + Mage::dispatchEvent('adminhtml_product_attribute_types', array('response'=>$response)); + $_disabledTypes = array(); + $_hiddenFields = array(); + foreach ($response->getTypes() as $type) { + $inputTypes[] = $type; + if (isset($type['hide_fields'])) { + $_hiddenFields[$type['value']] = $type['hide_fields']; + } + if (isset($type['disabled_types'])) { + $_disabledTypes[$type['value']] = $type['disabled_types']; + } + } + + if (Mage::registry('attribute_type_hidden_fields') === null) { + Mage::register('attribute_type_hidden_fields', $_hiddenFields); + } + if (Mage::registry('attribute_type_disabled_types') === null) { + Mage::register('attribute_type_disabled_types', $_disabledTypes); + } + + return array_merge(parent::toOptionArray(), $inputTypes); + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Link/Api.php b/app/code/core/Mage/Catalog/Model/Product/Link/Api.php index d8006bc6ff..d49fc4e25d 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Link/Api.php +++ b/app/code/core/Mage/Catalog/Model/Product/Link/Api.php @@ -33,6 +33,11 @@ */ class Mage_Catalog_Model_Product_Link_Api extends Mage_Catalog_Model_Api_Resource { + /** + * Product link type mapping, used for references and validation + * + * @var array + */ protected $_typeMap = array( 'related' => Mage_Catalog_Model_Product_Link::LINK_TYPE_RELATED, 'up_sell' => Mage_Catalog_Model_Product_Link::LINK_TYPE_UPSELL, @@ -50,6 +55,7 @@ public function __construct() * * @param string $type * @param int|sku $productId + * @param string $identifierType * @return array */ public function items($type, $productId, $identifierType = null) @@ -90,6 +96,7 @@ public function items($type, $productId, $identifierType = null) * @param int|string $productId * @param int|string $linkedProductId * @param array $data + * @param string $identifierType * @return boolean */ public function assign($type, $productId, $linkedProductId, $data = array(), $identifierType = null) @@ -118,7 +125,20 @@ public function assign($type, $productId, $linkedProductId, $data = array(), $id } try { - $link->getResource()->saveProductLinks($product, $links, $typeId); + if ($type == 'grouped') { + $link->getResource()->saveGroupedLinks($product, $links, $typeId); + } else { + $link->getResource()->saveProductLinks($product, $links, $typeId); + } + + $_linkInstance = Mage::getSingleton('catalog/product_link'); + $_linkInstance->saveProductRelations($product); + + $indexerStock = Mage::getModel('cataloginventory/stock_status'); + $indexerStock->updateStatus($productId); + + $indexerPrice = Mage::getResourceModel('catalog/product_indexer_price'); + $indexerPrice->reindexProductIds($productId); } catch (Exception $e) { $this->_fault('data_invalid', Mage::helper('catalog')->__('Link product does not exist.')); } @@ -133,6 +153,7 @@ public function assign($type, $productId, $linkedProductId, $data = array(), $id * @param int|string $productId * @param int|string $linkedProductId * @param array $data + * @param string $identifierType * @return boolean */ public function update($type, $productId, $linkedProductId, $data = array(), $identifierType = null) @@ -160,7 +181,20 @@ public function update($type, $productId, $linkedProductId, $data = array(), $id } try { - $link->getResource()->saveProductLinks($product, $links, $typeId); + if ($type == 'grouped') { + $link->getResource()->saveGroupedLinks($product, $links, $typeId); + } else { + $link->getResource()->saveProductLinks($product, $links, $typeId); + } + + $_linkInstance = Mage::getSingleton('catalog/product_link'); + $_linkInstance->saveProductRelations($product); + + $indexerStock = Mage::getModel('cataloginventory/stock_status'); + $indexerStock->updateStatus($productId); + + $indexerPrice = Mage::getResourceModel('catalog/product_indexer_price'); + $indexerPrice->reindexProductIds($productId); } catch (Exception $e) { $this->_fault('data_invalid', Mage::helper('catalog')->__('Link product does not exist.')); } @@ -174,6 +208,7 @@ public function update($type, $productId, $linkedProductId, $data = array(), $id * @param string $type * @param int|string $productId * @param int|string $linkedProductId + * @param string $identifierType * @return boolean */ public function remove($type, $productId, $linkedProductId, $identifierType = null) @@ -258,7 +293,7 @@ protected function _getTypeId($type) } /** - * Initilize and return product model + * Initialize and return product model * * @param int $productId * @param string $identifierType diff --git a/app/code/core/Mage/Catalog/Model/Product/Link/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Link/Api/V2.php index 53d35d27c7..1f0aa83ae7 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Link/Api/V2.php +++ b/app/code/core/Mage/Catalog/Model/Product/Link/Api/V2.php @@ -67,9 +67,24 @@ public function assign($type, $productId, $linkedProductId, $data = array(), $id } try { - $link->getResource()->saveProductLinks($product, $links, $typeId); + if ($type == 'grouped') { + $link->getResource()->saveGroupedLinks($product, $links, $typeId); + } else { + $link->getResource()->saveProductLinks($product, $links, $typeId); + } + + $_linkInstance = Mage::getSingleton('catalog/product_link'); + $_linkInstance->saveProductRelations($product); + + $indexerStock = Mage::getModel('cataloginventory/stock_status'); + $indexerStock->updateStatus($productId); + + $indexerPrice = Mage::getResourceModel('catalog/product_indexer_price'); + $indexerPrice->reindexProductIds($productId); + } catch (Exception $e) { - $this->_fault('data_invalid', Mage::helper('catalog')->__('Link product does not exist.')); + $this->_fault('data_invalid', $e->getMessage()); + //$this->_fault('data_invalid', Mage::helper('catalog')->__('Link product does not exist.')); } return true; @@ -109,7 +124,21 @@ public function update($type, $productId, $linkedProductId, $data = array(), $id } try { - $link->getResource()->saveProductLinks($product, $links, $typeId); + if ($type == 'grouped') { + $link->getResource()->saveGroupedLinks($product, $links, $typeId); + } else { + $link->getResource()->saveProductLinks($product, $links, $typeId); + } + + $_linkInstance = Mage::getSingleton('catalog/product_link'); + $_linkInstance->saveProductRelations($product); + + $indexerStock = Mage::getModel('cataloginventory/stock_status'); + $indexerStock->updateStatus($productId); + + $indexerPrice = Mage::getResourceModel('catalog/product_indexer_price'); + $indexerPrice->reindexProductIds($productId); + } catch (Exception $e) { $this->_fault('data_invalid', Mage::helper('catalog')->__('Link product does not exist.')); } diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Api.php b/app/code/core/Mage/Catalog/Model/Product/Option/Api.php new file mode 100644 index 0000000000..6718827bed --- /dev/null +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Api.php @@ -0,0 +1,327 @@ + + */ +class Mage_Catalog_Model_Product_Option_Api extends Mage_Catalog_Model_Api_Resource +{ + + /** + * Add custom option to product + * + * @param string $productId + * @param array $data + * @param int|string|null $store + * @return bool $isAdded + */ + public function add($productId, $data, $store = null) + { + $product = $this->_getProduct($productId, $store, null); + if (!(is_array($data['additional_fields']) and count($data['additional_fields']))) { + $this->_fault('invalid_data'); + } + if (!$this->_isTypeAllowed($data['type'])) { + $this->_fault('invalid_type'); + } + $this->_prepareAdditionalFields( + $data, + $product->getOptionInstance()->getGroupByType($data['type']) + ); + $this->_saveProductCustomOption($product, $data); + return true; + } + + /** + * Update product custom option data + * + * @param string $optionId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function update($optionId, $data, $store = null) + { + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = Mage::getModel('catalog/product_option')->load($optionId); + if (!$option->getId()) { + $this->_fault('option_not_exists'); + } + $product = $this->_getProduct($option->getProductId(), $store, null); + $option = $product->getOptionById($optionId); + if (isset($data['type']) and !$this->_isTypeAllowed($data['type'])) { + $this->_fault('invalid_type'); + } + if (isset($data['additional_fields'])) { + $this->_prepareAdditionalFields( + $data, + $option->getGroupByType() + ); + } + foreach ($option->getValues() as $valueId => $value) { + if(isset($data['values'][$valueId])) { + $data['values'][$valueId] = array_merge($value->getData(), $data['values'][$valueId]); + } + } + $data = array_merge($option->getData(), $data); + $this->_saveProductCustomOption($product, $data); + return true; + } + + /** + * Prepare custom option data for saving by model. Used for custom option add and update + * + * @param array $data + * @param string $groupType + * @return void + */ + protected function _prepareAdditionalFields(&$data, $groupType) + { + if (is_array($data['additional_fields'])) { + if ($groupType != Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { + // reset can be used as there should be the only + // element in 'additional_fields' for options of all types except those from Select group + $field = reset($data['additional_fields']); + if (!(is_array($field) and count($field))) { + $this->_fault('invalid_data'); + } else { + foreach ($field as $key => $value) { + $data[$key] = $value; + } + } + } else { + // convert Select rows array to appropriate format for saving in the model + foreach ($data['additional_fields'] as $row) { + if (!(is_array($row) and count($row))) { + $this->_fault('invalid_data'); + } else { + foreach ($row as $key => $value) { + $row[$key] = Mage::helper('catalog')->stripTags($value); + } + if (!empty($row['value_id'])) { + // map 'value_id' to 'option_type_id' + $row['option_type_id'] = $row['value_id']; + unset($row['value_id']); + $data['values'][$row['option_type_id']] = $row; + } else { + $data['values'][] = $row; + } + } + } + } + } + unset($data['additional_fields']); + } + + /** + * Save product custom option data. Used for custom option add and update. + * + * @param Mage_Catalog_Model_Product $product + * @param array $data + * @return void + */ + protected function _saveProductCustomOption($product, $data) + { + foreach ($data as $key => $value) { + if (is_string($value)) { + $data[$key] = Mage::helper('catalog')->stripTags($value); + } + } + // setProductOptions expects data to be an array of options arrays + $data = array($data); + if (!$product->getOptionsReadonly()) { + $product->setProductOptions($data); + } + $product->setCanSaveCustomOptions(!$product->getOptionsReadonly()); + try { + // an empty request can be set as event parameter + // because it is not used for options changing in observers + Mage::dispatchEvent( + 'catalog_product_prepare_save', + array('product' => $product, 'request' => new Mage_Core_Controller_Request_Http()) + ); + $product->save(); + } catch (Exception $e) { + $this->_fault('save_option_error', $e->getMessage()); + } + } + + /** + * Read list of possible custom option types from module config + * + * @return array + */ + public function types() + { + $path = Mage_Adminhtml_Model_System_Config_Source_Product_Options_Type::PRODUCT_OPTIONS_GROUPS_PATH; + $types = array(); + foreach (Mage::getConfig()->getNode($path)->children() as $group) { + $groupTypes = Mage::getConfig()->getNode($path . '/' . $group->getName() . '/types')->children(); + /** @var $type Mage_Core_Model_Config_Element */ + foreach($groupTypes as $type){ + $labelPath = $path . '/' . $group->getName() . '/types/' . $type->getName() . '/label'; + $types[] = array( + 'label' => (string) Mage::getConfig()->getNode($labelPath), + 'value' => $type->getName() + ); + } + } + return $types; + } + + /** + * Get full information about custom option in product + * + * @param int|string $optionId + * @param int|string|null $store + * @return array + */ + public function info($optionId, $store = null) + { + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = Mage::getModel('catalog/product_option')->load($optionId); + if (!$option->getId()) { + $this->_fault('option_not_exists'); + } + /** @var $product Mage_Catalog_Model_Product */ + $product = $this->_getProduct($option->getProductId(), $store, null); + $option = $product->getOptionById($optionId); + $result = array( + 'title' => $option->getTitle(), + 'type' => $option->getType(), + 'is_require' => $option->getIsRequire(), + 'sort_order' => $option->getSortOrder(), + // additional_fields should be two-dimensional array for all option types + 'additional_fields' => array( + array( + 'price' => $option->getPrice(), + 'price_type' => $option->getPriceType(), + 'sku' => $option->getSku() + ) + ) + ); + // Set additional fields to each type group + switch ($option->getGroupByType()) { + case Mage_Catalog_Model_Product_Option::OPTION_GROUP_TEXT: + $result['additional_fields'][0]['max_characters'] = $option->getMaxCharacters(); + break; + case Mage_Catalog_Model_Product_Option::OPTION_GROUP_FILE: + $result['additional_fields'][0]['file_extension'] = $option->getFileExtension(); + $result['additional_fields'][0]['image_size_x'] = $option->getImageSizeX(); + $result['additional_fields'][0]['image_size_y'] = $option->getImageSizeY(); + break; + case Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT: + $result['additional_fields'] = array(); + foreach ($option->getValuesCollection() as $value) { + $result['additional_fields'][] = array( + 'value_id' => $value->getId(), + 'title' => $value->getTitle(), + 'price' => $value->getPrice(), + 'price_type' => $value->getPriceType(), + 'sku' => $value->getSku(), + 'sort_order' => $value->getSortOrder() + ); + } + break; + default: + break; + } + + return $result; + } + + /** + * Retrieve list of product custom options + * + * @param string $productId + * @param int|string|null $store + * @return array + */ + public function items($productId, $store = null) + { + $result = array(); + $product = $this->_getProduct($productId, $store, null); + /** @var $option Mage_Catalog_Model_Product_Option */ + foreach ($product->getProductOptionsCollection() as $option) { + $result[] = array( + 'option_id' => $option->getId(), + 'title' => $option->getTitle(), + 'type' => $option->getType(), + 'is_require' => $option->getIsRequire(), + 'sort_order' => $option->getSortOrder() + ); + } + return $result; + } + + /** + * Remove product custom option + * + * @param string $optionId + * @return boolean + */ + public function remove($optionId) + { + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = Mage::getModel('catalog/product_option')->load($optionId); + if (!$option->getId()) { + $this->_fault('option_not_exists'); + } + try { + $option->getValueInstance()->deleteValue($optionId); + $option->deletePrices($optionId); + $option->deleteTitles($optionId); + $option->delete(); + } catch (Exception $e){ + $this->fault('delete_option_error'); + } + return true; + } + + /** + * Check is type in allowed set + * + * @param string $type + * @return bool + */ + protected function _isTypeAllowed($type) + { + $allowedTypes = array(); + foreach($this->types() as $optionType){ + $allowedTypes[] = $optionType['value']; + } + + if (!in_array($type, $allowedTypes)) { + return false; + } + return true; + } + +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Option/Api/V2.php new file mode 100644 index 0000000000..d0e1c323ea --- /dev/null +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Api/V2.php @@ -0,0 +1,81 @@ + + */ +class Mage_Catalog_Model_Product_Option_Api_V2 extends Mage_Catalog_Model_Product_Option_Api +{ + + /** + * Add custom option to product + * + * @param string $productId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function add($productId, $data, $store = null) + { + Mage::helper('api')->toArray($data); + return parent::add($productId, $data, $store); + } + + /** + * Update product custom option data + * + * @param string $optionId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function update($optionId, $data, $store = null) + { + Mage::helper('api')->toArray($data); + return parent::update($optionId, $data, $store); + } + + /** + * Retrieve list of product custom options + * + * @param string $productId + * @param int|string|null $store + * @return array + */ + public function items($productId, $store) + { + $result = parent::items($productId, $store); + foreach ($result as $key => $option) { + $result[$key] = Mage::helper('api')->wsiArrayPacker($option); + } + return $result; + } + +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Value/Api.php b/app/code/core/Mage/Catalog/Model/Product/Option/Value/Api.php new file mode 100644 index 0000000000..669fa8aa2e --- /dev/null +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Value/Api.php @@ -0,0 +1,226 @@ + + */ +class Mage_Catalog_Model_Product_Option_Value_Api extends Mage_Catalog_Model_Api_Resource +{ + /** + * Retrieve values from specified option + * + * @param string $optionId + * @param int|string|null $store + * @return array + */ + public function items($optionId, $store = null) + { + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = $this->_prepareOption($optionId, $store); + $productOptionValues = $option->getValuesCollection(); + $result = array(); + foreach($productOptionValues as $value){ + $result[] = array( + 'value_id' => $value->getId(), + 'title' => $value->getTitle(), + 'price' => $value->getPrice(), + 'price_type' => $value->getPriceType(), + 'sku' => $value->getSku(), + 'sort_order' => $value->getSortOrder() + ); + } + return $result; + } + + /** + * Retrieve specified option value info + * + * @param string $valueId + * @param int|string|null $store + * @return array + */ + public function info($valueId, $store = null) + { + /** @var $productOptionValue Mage_Catalog_Model_Product_Option_Value */ + $productOptionValue = Mage::getModel('catalog/product_option_value')->load($valueId); + if (!$productOptionValue->getId()) { + $this->_fault('value_not_exists'); + } + $storeId = $this->_getStoreId($store); + $productOptionValues = $productOptionValue + ->getValuesByOption( + array($valueId), + $productOptionValue->getOptionId(), + $storeId + ) + ->addTitleToResult($storeId) + ->addPriceToResult($storeId); + + $result = $productOptionValues->toArray(); + // reset can be used as the only item is expected + $result = reset($result['items']); + if (empty($result)) { + $this->_fault('value_not_exists'); + } + // map option_type_id to value_id + $result['value_id'] = $result['option_type_id']; + unset($result['option_type_id']); + return $result; + } + + /** + * Add new values to select option + * + * @param string $optionId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function add($optionId, $data, $store = null) + { + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = $this->_prepareOption($optionId, $store); + /** @var $optionValueModel Mage_Catalog_Model_Product_Option_Value */ + $optionValueModel = Mage::getModel('catalog/product_option_value'); + $optionValueModel->setOption($option); + foreach ($data as &$optionValue) { + foreach ($optionValue as &$value) { + $value = Mage::helper('catalog')->stripTags($value); + } + } + $optionValueModel->setValues($data); + try { + $optionValueModel->saveValues(); + } catch (Exception $e) { + $this->_fault('add_option_value_error', $e->getMessage()); + } + return true; + } + + /** + * Update value to select option + * + * @param string $valueId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function update($valueId, $data, $store = null) + { + /** @var $productOptionValue Mage_Catalog_Model_Product_Option_Value */ + $productOptionValue = Mage::getModel('catalog/product_option_value')->load($valueId); + if (!$productOptionValue->getId()) { + $this->_fault('value_not_exists'); + } + + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = $this->_prepareOption($productOptionValue->getOptionId(), $store); + if (!$option->getId()) { + $this->_fault('option_not_exists'); + } + $productOptionValue->setOption($option); + // Sanitize data + foreach ($data as $key => $value) { + $data[$key] = Mage::helper('catalog')->stripTags($value); + } + if (!isset($data['title']) OR empty($data['title'])) { + $this->_fault('option_value_title_required'); + } + $data['option_type_id'] = $valueId; + $data['store_id'] = $this->_getStoreId($store); + $productOptionValue->addValue($data); + $productOptionValue->setData($data); + + try { + $productOptionValue->save()->saveValues(); + } catch (Exception $e) { + $this->_fault('update_option_value_error', $e->getMessage()); + } + + return true; + } + + /** + * Delete value from select option + * + * @param int $valueId + * @return boolean + */ + public function remove($valueId) + { + /** @var $optionValue Mage_Catalog_Model_Product_Option_Value */ + $optionValue = Mage::getModel('catalog/product_option_value')->load($valueId); + if (!$optionValue->getId()) { + $this->_fault('value_not_exists'); + } + + // check values count + if(count($this->items($optionValue->getOptionId())) <= 1){ + $this->_fault('cant_delete_last_value'); + } + + try { + $optionValue->deleteValues($valueId); + } catch (Mage_Core_Exception $e) { + $this->_fault('not_deleted', $e->getMessage()); + } + + return true; + } + + /** + * Load option by id and store + * + * @param string $optionId + * @param int|string|null $store + * @return Mage_Catalog_Model_Product_Option + */ + protected function _prepareOption($optionId, $store = null) + { + /** @var $option Mage_Catalog_Model_Product_Option */ + $option = Mage::getModel('catalog/product_option'); + if (is_string($store) || is_integer($store)) { + $storeId = $this->_getStoreId($store); + $option->setStoreId($storeId); + } + $option->load($optionId); + if (isset($storeId)) { + $option->setData('store_id', $storeId); + } + if (!$option->getId()) { + $this->_fault('option_not_exists'); + } + if ($option->getGroupByType() != Mage_Catalog_Model_Product_Option::OPTION_GROUP_SELECT) { + $this->_fault('invalid_option_type'); + } + return $option; + } + +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Option/Value/Api/V2.php b/app/code/core/Mage/Catalog/Model/Product/Option/Value/Api/V2.php new file mode 100644 index 0000000000..3d0c0d4f43 --- /dev/null +++ b/app/code/core/Mage/Catalog/Model/Product/Option/Value/Api/V2.php @@ -0,0 +1,104 @@ + + */ +class Mage_Catalog_Model_Product_Option_Value_Api_V2 extends Mage_Catalog_Model_Product_Option_Value_Api +{ + /** + * Retrieve values from specified option + * + * @param string $optionId + * @param int|string|null $store + * @return array + */ + public function items($optionId, $store = null) + { + $result = parent::items($optionId, $store); + foreach ($result as $key => $optionValue) { + $result[$key] = Mage::helper('api')->wsiArrayPacker($optionValue); + } + return $result; + } + + /** + * Retrieve specified option value info + * + * @param string $valueId + * @param int|string|null $store + * @return array + */ + public function info($valueId, $store = null) + { + return Mage::helper('api')->wsiArrayPacker( + parent::info($valueId, $store) + ); + } + + /** + * Add new values to select option + * + * @param string $optionId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function add($optionId, $data, $store = null) + { + Mage::helper('api')->toArray($data); + return parent::add($optionId, $data, $store); + } + + /** + * Update value to select option + * + * @param string $valueId + * @param array $data + * @param int|string|null $store + * @return bool + */ + public function update($valueId, $data, $store = null) + { + Mage::helper('api')->toArray($data); + return parent::update($valueId, $data, $store); + } + + /** + * Delete value from select option + * + * @param int $valueId + * @return boolean + */ + public function remove($valueId) + { + return parent::remove($valueId); + } +} diff --git a/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php b/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php index 77ba320dfd..c45a79cb9a 100644 --- a/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php +++ b/app/code/core/Mage/Catalog/Model/Product/Type/Configurable.php @@ -452,6 +452,9 @@ public function isSalable($product = null) if ($salable !== false) { $salable = false; + if (!is_null($product)) { + $this->setStoreFilter($product->getStoreId(), $product); + } foreach ($this->getUsedProducts(null, $product) as $child) { if ($child->isSalable()) { $salable = true; diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product.php b/app/code/core/Mage/Catalog/Model/Resource/Product.php index bec4a8ab06..526bc37b5c 100755 --- a/app/code/core/Mage/Catalog/Model/Resource/Product.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product.php @@ -518,9 +518,11 @@ public function getCategoryCollection($product) */ public function getAvailableInCategories($object) { - $select = $this->_getReadAdapter()->select() + // is_parent=1 ensures that we'll get only category IDs those are direct parents of the product, instead of + // fetching all parent IDs, including those are higher on the tree + $select = $this->_getReadAdapter()->select()->distinct() ->from($this->getTable('catalog/category_product_index'), array('category_id')) - ->where('product_id = ?', (int)$object->getEntityId()); + ->where('product_id = ? AND is_parent = 1', (int)$object->getEntityId()); return $this->_getReadAdapter()->fetchCol($select); } diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price.php index 4c112ddf6d..099a1a1393 100755 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price.php @@ -376,7 +376,13 @@ public function reindexAll() $indexers = $this->getTypeIndexers(); foreach ($indexers as $indexer) { /** @var $indexer Mage_Catalog_Model_Resource_Product_Indexer_Price_Interface */ + if (!$this->_allowTableChanges && is_callable(array($indexer, 'setAllowTableChanges'))) { + $indexer->setAllowTableChanges(false); + } $indexer->reindexAll(); + if (!$this->_allowTableChanges && is_callable(array($indexer, 'setAllowTableChanges'))) { + $indexer->setAllowTableChanges(true); + } } $this->syncData(); diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Configurable.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Configurable.php index 25c69dfd03..b3f8208617 100755 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Configurable.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Configurable.php @@ -208,7 +208,7 @@ protected function _applyConfigurableOption() $query = $select->crossUpdateFromSelect($table); $write->query($query); - if ($this->useIdxTable()) { + if ($this->useIdxTable() && $this->_allowTableChanges) { $write->truncateTable($coaTable); $write->truncateTable($copTable); } else { diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Default.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Default.php index 43e732f756..542b0fa6b8 100755 --- a/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Default.php +++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Indexer/Price/Default.php @@ -496,7 +496,7 @@ protected function _applyCustomOption() $query = $select->crossUpdateFromSelect($table); $write->query($query); - if ($this->useIdxTable()) { + if ($this->useIdxTable() && $this->_allowTableChanges) { $write->truncateTable($coaTable); $write->truncateTable($copTable); } else { @@ -534,7 +534,7 @@ protected function _movePriceDataToIndexTable() $query = $select->insertFromSelect($this->getIdxTable(), array(), false); $write->query($query); - if ($this->useIdxTable()) { + if ($this->useIdxTable() && $this->_allowTableChanges) { $write->truncateTable($table); } else { $write->delete($table); diff --git a/app/code/core/Mage/Catalog/etc/api.xml b/app/code/core/Mage/Catalog/etc/api.xml index b90b159f7e..07044afebb 100644 --- a/app/code/core/Mage/Catalog/etc/api.xml +++ b/app/code/core/Mage/Catalog/etc/api.xml @@ -174,16 +174,21 @@ Set special price catalog/product/update + + Get list of non-default attributes by product type and attributes set + getAdditionalAttributes + catalog/product/listOfAdditionalAttributes + 100 Requested store view not found. - + 101 Product not exists. - + 102 Invalid data given. Details in error message. @@ -192,6 +197,18 @@ 103 Product not deleted. Details in error message. + + 104 + Product type is not in allowed types. + + + 105 + Product attribute set is not existed + + + 106 + Product attribute set is not belong catalog product entity type + @@ -212,6 +229,34 @@ Retrieve attribute options catalog/product/attribute/read + + Get list of possible attribute types + catalog/product/attribute/types + + + Create new attribute + catalog/product/attribute/create + + + Update attribute + catalog/product/attribute/update + + + Delete attribute + catalog/product/attribute/remove + + + Get full information about attribute with list of options + catalog/product/attribute/info + + + Add option + catalog/product/attribute/option/add + + + Remove option + catalog/product/attribute/option/remove + @@ -222,19 +267,152 @@ 101 Requested attribute not found. + + 102 + Invalid request parameters. + + + 103 + Attribute code is invalid. Please use only letters (a-z), numbers (0-9) or underscore(_) in this field, first character should be a letter. + + + 104 + Incorrect attribute type. + + + 105 + Unable to save attribute. + + + 106 + This attribute cannot be deleted. + + + 107 + This attribute cannot be edited. + + + 108 + Unable to add option. + + + 109 + Unable to remove option. + Product attribute sets API catalog/product_attribute_set_api - catalog/product + catalog/product/attribute/set + + Create attribute set based on another set + catalog/product/attribute/set/create + + + Remove attribute set + catalog/product/attribute/set/remove + Retrieve product attribute sets items + catalog/product/attribute/set/list + + Add attribute into attribute set + catalog/product/attribute/set/attribute_add + + + Remove attribute from attribute set + catalog/product/attribute/set/attribute_remove + + + Add group into attribute set + catalog/product/attribute/set/group_add + + + Rename existing group + catalog/product/attribute/set/group_rename + + + Remove group from attribute set + catalog/product/attribute/set/group_remove + + + 100 + Attribute set with requested id does not exist. + + + 101 + Invalid data given. + + + 102 + Error while creating attribute set. Details in error message. + + + 103 + Error while removing attribute set. Details in error message. + + + 104 + Attribute set with requested id does not exist. + + + 105 + Unable to remove attribute set as it has related goods. Use forceProductsRemove parameter to remove attribute set with all goods. + + + 106 + Attribute with requested id does not exist. + + + 107 + Error while adding attribute to attribute set. Details in error message. + + + 108 + Attribute group with requested id does not exist. + + + 109 + Requested attribute is already in requested attribute set. + + + 110 + Error while removing attribute from attribute set. Details in error message. + + + 111 + Requested attribute is not in requested attribute set. + + + 112 + Requested group exist already in requested attribute set. + + + 113 + Error while adding group to attribute set. Details in error message. + + + 114 + Error while renaming group. Details in error message. + + + 115 + Error while removing group from attribute set. Details in error message. + + + 116 + Group can not be removed as it contains system attributes. + + + 117 + Group can not be removed as it contains attributes, used in configurable products. + @@ -391,6 +569,134 @@ + + Catalog product custom options API + catalog/product_option_api + catalog/product/option + + + Add new custom option into product + catalog/product/option/add + + + Update custom option of product + catalog/product/option/update + + + Get list of available custom option types + catalog/product/option/types + + + Get full information about custom option in product + catalog/product/option/info + + + Retrieve list of product custom options + catalog/product/option/list + items + + + Remove custom option + catalog/product/option/remove + + + + + 101 + Product with requested id does not exist. + + + 102 + Provided data is invalid. + + + 103 + Error while saving an option. Details are in the error message. + + + 104 + Store with requested code/id does not exist. + + + 105 + Option with requested id does not exist. + + + 106 + Invalid option type provided. Call 'types' to get list of allowed option types. + + + 107 + Error while deleting an option. Details are in the error message. + + + + + Catalog product custom option values API + catalog/product_option_value_api + catalog/product/option/value + + + Retrieve list of option values + items + catalog/product/option/value/list + + + Retrieve option value info + catalog/product/option/value/info + + + Add new values into custom option + catalog/product/option/value/add + + + Update value of custom option + catalog/product/option/value/update + + + Remove value from custom option + catalog/product/option/value/remove + + + + + 101 + Option value with requested id does not exist. + + + 102 + Error while adding an option value. Details are in the error message. + + + 103 + Option with requested id does not exist. + + + 104 + Invalid option type. + + + 105 + Store with requested code/id does not exist. + + + 106 + Can not delete option. + + + 107 + Error while updating an option value. Details are in the error message. + + + 108 + Title field is required. + + + 109 + Option should have at least one value. Can not delete last value. + + + catalog_category @@ -404,6 +710,8 @@ catalog_product_attribute_media catalog_product_attribute_tier_price catalog_product_attribute_tier_price + catalog_product_custom_option + catalog_product_custom_option_value @@ -416,8 +724,32 @@ catalogProductAttributeTierPrice catalogProductAttributeMedia catalogProductLink + catalogProductCustomOption + catalogProductCustomOptionValue + + + + + tree + + + + + remove + + + + + assign + + + remove + + + + @@ -443,6 +775,10 @@ Retrieve category data + + Allowed Attributes (REST Only) + 0 + Assigned Products 100 @@ -474,6 +810,13 @@ Retrieve products data + + Get list of non-default attributes by product type and attributes set + + + Allowed Attributes (REST Only) + 0 + Product Attributes 100 @@ -483,6 +826,57 @@ Change or Retrieve attribute store view + + Get list of possible attribute types + + + Create + + + Update + + + Remove + + + Get full information about attribute with list of options + + + + Attribute Sets + + List + + + Create + + + Remove + + + Attribute add + + + Attribute remove + + + Group add + + + Rename group + + + Group remove + + Link (Related, Up sell, Cross sell) @@ -510,6 +904,47 @@ Remove + diff --git a/app/code/core/Mage/Catalog/etc/wsdl.xml b/app/code/core/Mage/Catalog/etc/wsdl.xml index a324953c9e..21a67e0cbc 100644 --- a/app/code/core/Mage/Catalog/etc/wsdl.xml +++ b/app/code/core/Mage/Catalog/etc/wsdl.xml @@ -15,6 +15,12 @@ + + + + + + @@ -98,7 +104,7 @@ - + @@ -367,6 +373,235 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -451,6 +686,14 @@ + + + + + + + + @@ -466,12 +709,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -547,7 +898,7 @@ - + @@ -556,7 +907,7 @@ - + @@ -576,7 +927,7 @@ - + @@ -587,7 +938,7 @@ - + @@ -597,7 +948,7 @@ - + @@ -619,6 +970,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -652,7 +1091,7 @@ - + @@ -857,6 +1296,11 @@ + + Get list of additional attributes which are not in default create/update list + + + Retrieve attribute list @@ -867,11 +1311,81 @@ + + Create product attribute set based on another set + + + + + Remove product attribute set + + + Retrieve product attribute sets + + Add attribute into attribute set + + + + + Remove attribute from attribute set + + + + + Create group within existing attribute set + + + + + Rename existing group + + + + + Remove group from attribute set + + + + + Get list of possible attribute types + + + + + Create new attribute + + + + + Delete attribute + + + + + Get full information about attribute with list of options + + + + + Update attribute + + + + + Add option to attribute + + + + + Remove option from attribute + + + Retrieve product types @@ -952,6 +1466,61 @@ + + Add new custom option into product + + + + + Update product custom option + + + + + Get list of available custom option types + + + + + Get full information about custom option in product + + + + + Retrieve list of product custom options + + + + + Remove custom option + + + + + Retrieve custom option value info + + + + + Retrieve custom option values list + + + + + Add new custom option values + + + + + Update custom option value + + + + + Remove value from custom option + + + @@ -1098,6 +1667,17 @@ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> + + + + + + + + + @@ -1186,6 +1766,17 @@ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> + + + + + + + + + @@ -1208,6 +1799,17 @@ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> + + + + + + + + + @@ -1219,7 +1821,7 @@ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1428,6 +2162,127 @@ encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/CatalogRule/Model/Rule.php b/app/code/core/Mage/CatalogRule/Model/Rule.php index 79d53fe945..0301f02702 100644 --- a/app/code/core/Mage/CatalogRule/Model/Rule.php +++ b/app/code/core/Mage/CatalogRule/Model/Rule.php @@ -316,6 +316,7 @@ public function getCustomerGroupIds() */ public function applyAll() { + $this->getResourceCollection()->walk(array($this->_getResource(), 'updateRuleProductData')); $this->_getResource()->applyAllRulesForDateRange(); $this->_invalidateCache(); $indexProcess = Mage::getSingleton('index/indexer')->getProcessByCode('catalog_product_price'); diff --git a/app/code/core/Mage/CatalogSearch/Model/Advanced.php b/app/code/core/Mage/CatalogSearch/Model/Advanced.php index b74baef11e..71306af5b2 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Advanced.php +++ b/app/code/core/Mage/CatalogSearch/Model/Advanced.php @@ -158,21 +158,25 @@ public function addFilters($values) $value = $values[$attribute->getAttributeCode()]; if ($attribute->getAttributeCode() == 'price') { - if ((isset($value['from']) && strlen(trim($value['from']))) || - (isset($value['to']) && strlen(trim($value['to'])))) { - if (isset($value['currency']) && !empty($value['currency'])) { + $value['from'] = isset($value['from']) ? trim($value['from']) : ''; + $value['to'] = isset($value['to']) ? trim($value['to']) : ''; + if (is_numeric($value['from']) || is_numeric($value['to'])) { + if (!empty($value['currency'])) { $rate = Mage::app()->getStore()->getBaseCurrency()->getRate($value['currency']); } else { $rate = 1; } - if ($this->_getResource()->addRatedPriceFilter($this->getProductCollection(), $attribute, $value, $rate)) { + if ($this->_getResource()->addRatedPriceFilter( + $this->getProductCollection(), $attribute, $value, $rate) + ) { $hasConditions = true; $this->_addSearchCriteria($attribute, $value); } } } else if ($attribute->isIndexable()) { if (!is_string($value) || strlen($value) != 0) { - if ($this->_getResource()->addIndexableAttributeModifiedFilter($this->getProductCollection(), $attribute, $value)) { + if ($this->_getResource()->addIndexableAttributeModifiedFilter( + $this->getProductCollection(), $attribute, $value)) { $hasConditions = true; $this->_addSearchCriteria($attribute, $value); } @@ -227,7 +231,8 @@ protected function _addSearchCriteria($attribute, $value) if (strlen($value['from']) > 0 && strlen($value['to']) > 0) { // - - $value = sprintf('%s - %s', ($currencyModel ? $from : $value['from']), ($currencyModel ? $to : $value['to'])); + $value = sprintf('%s - %s', + ($currencyModel ? $from : $value['from']), ($currencyModel ? $to : $value['to'])); } elseif (strlen($value['from']) > 0) { // and more $value = Mage::helper('catalogsearch')->__('%s and greater', ($currencyModel ? $from : $value['from'])); @@ -241,7 +246,9 @@ protected function _addSearchCriteria($attribute, $value) } } - if (($attribute->getFrontendInput() == 'select' || $attribute->getFrontendInput() == 'multiselect') && is_array($value)) { + if (($attribute->getFrontendInput() == 'select' || $attribute->getFrontendInput() == 'multiselect') + && is_array($value) + ) { foreach ($value as $key => $val){ $value[$key] = $attribute->getSource()->getOptionText($val); diff --git a/app/code/core/Mage/CatalogSearch/Model/Fulltext.php b/app/code/core/Mage/CatalogSearch/Model/Fulltext.php index 76d5e45cf5..12e3d7370c 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Fulltext.php +++ b/app/code/core/Mage/CatalogSearch/Model/Fulltext.php @@ -47,6 +47,13 @@ class Mage_CatalogSearch_Model_Fulltext extends Mage_Core_Model_Abstract const SEARCH_TYPE_COMBINE = 3; const XML_PATH_CATALOG_SEARCH_TYPE = 'catalog/search/search_type'; + /** + * Whether table changes are allowed + * + * @var bool + */ + protected $_allowTableChanges = true; + protected function _construct() { $this->_init('catalogsearch/fulltext'); @@ -72,7 +79,14 @@ public function rebuildIndex($storeId = null, $productIds = null) 'product_ids' => $productIds )); - $this->getResource()->rebuildIndex($storeId, $productIds); + $resourceModel = $this->getResource(); + if (!$this->_allowTableChanges && is_callable(array($resourceModel, 'setAllowTableChanges'))) { + $resourceModel->setAllowTableChanges(false); + } + $resourceModel->rebuildIndex($storeId, $productIds); + if (!$this->_allowTableChanges && is_callable(array($resourceModel, 'setAllowTableChanges'))) { + $resourceModel->setAllowTableChanges(true); + } Mage::dispatchEvent('catalogsearch_index_process_complete', array()); @@ -105,7 +119,15 @@ public function cleanIndex($storeId = null, $productId = null) */ public function resetSearchResults() { - $this->getResource()->resetSearchResults(); + $resourceModel = $this->getResource(); + if (!$this->_allowTableChanges && is_callable(array($resourceModel, 'setAllowTableChanges'))) { + $resourceModel->setAllowTableChanges(false); + } + $resourceModel->resetSearchResults(); + if (!$this->_allowTableChanges && is_callable(array($resourceModel, 'setAllowTableChanges'))) { + $resourceModel->setAllowTableChanges(true); + } + return $this; } @@ -151,4 +173,16 @@ public function updateCategoryIndex($productIds, $categoryIds) $this->getResource()->updateCategoryIndex($productIds, $categoryIds); return $this; } + + /** + * Set whether table changes are allowed + * + * @param bool $value + * @return Mage_CatalogSearch_Model_Fulltext + */ + public function setAllowTableChanges($value = true) + { + $this->_allowTableChanges = $value; + return $this; + } } diff --git a/app/code/core/Mage/CatalogSearch/Model/Indexer/Fulltext.php b/app/code/core/Mage/CatalogSearch/Model/Indexer/Fulltext.php index dd0232df4a..f7c813257d 100644 --- a/app/code/core/Mage/CatalogSearch/Model/Indexer/Fulltext.php +++ b/app/code/core/Mage/CatalogSearch/Model/Indexer/Fulltext.php @@ -314,6 +314,9 @@ protected function _isProductComposite($productId) */ protected function _processEvent(Mage_Index_Model_Event $event) { + if (!$this->_allowTableChanges && is_callable(array($this->_getIndexer(), 'setAllowTableChanges'))) { + $this->_getIndexer()->setAllowTableChanges(false); + } $data = $event->getNewData(); if (!empty($data['catalogsearch_fulltext_reindex_all'])) { @@ -384,6 +387,9 @@ protected function _processEvent(Mage_Index_Model_Event $event) $this->_getIndexer() ->updateCategoryIndex($productIds, $categoryIds); } + if (!$this->_allowTableChanges && is_callable(array($this->_getIndexer(), 'setAllowTableChanges'))) { + $this->_getIndexer()->setAllowTableChanges(true); + } } /** diff --git a/app/code/core/Mage/CatalogSearch/Model/Resource/Advanced.php b/app/code/core/Mage/CatalogSearch/Model/Resource/Advanced.php index 6346123bda..5e5f79a58c 100755 --- a/app/code/core/Mage/CatalogSearch/Model/Resource/Advanced.php +++ b/app/code/core/Mage/CatalogSearch/Model/Resource/Advanced.php @@ -117,10 +117,12 @@ public function addRatedPriceFilter($collection, $attribute, $value, $rate = 1) $conditions = array(); if (strlen($value['from']) > 0) { - $conditions[] = $adapter->quoteInto('price_index.min_price %s * %s >= ?', $value['from']); + $conditions[] = $adapter->quoteInto( + 'price_index.min_price %s * %s >= ?', $value['from'], Zend_Db::FLOAT_TYPE); } if (strlen($value['to']) > 0) { - $conditions[] = $adapter->quoteInto('price_index.min_price %s * %s <= ?', $value['to']); + $conditions[] = $adapter->quoteInto( + 'price_index.min_price %s * %s <= ?', $value['to'], Zend_Db::FLOAT_TYPE); } if (!$conditions) { diff --git a/app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php b/app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php index 0259680159..01f27a0c6d 100755 --- a/app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php +++ b/app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php @@ -69,6 +69,13 @@ class Mage_CatalogSearch_Model_Resource_Fulltext extends Mage_Core_Model_Resourc */ protected $_engine = null; + /** + * Whether table changes are allowed + * + * @var bool + */ + protected $_allowTableChanges = true; + /** * Init resource model * @@ -294,7 +301,11 @@ public function resetSearchResults() { $adapter = $this->_getWriteAdapter(); $adapter->update($this->getTable('catalogsearch/search_query'), array('is_processed' => 0)); - $adapter->truncateTable($this->getTable('catalogsearch/result')); + if ($this->_allowTableChanges) { + $adapter->truncateTable($this->getTable('catalogsearch/result')); + } else { + $adapter->delete($this->getTable('catalogsearch/result')); + } Mage::dispatchEvent('catalogsearch_reset_search_result'); @@ -764,4 +775,16 @@ protected function _getStoreDate($storeId, $date = null) return null; } + + /** + * Set whether table changes are allowed + * + * @param bool $value + * @return Mage_CatalogSearch_Model_Resource_Fulltext + */ + public function setAllowTableChanges($value = true) + { + $this->_allowTableChanges = $value; + return $this; + } } diff --git a/app/code/core/Mage/Checkout/Model/Api/Resource.php b/app/code/core/Mage/Checkout/Model/Api/Resource.php index de11ecde38..2d55f060c9 100644 --- a/app/code/core/Mage/Checkout/Model/Api/Resource.php +++ b/app/code/core/Mage/Checkout/Model/Api/Resource.php @@ -119,7 +119,7 @@ protected function _getQuote($quoteId, $store = null) /** @var $quote Mage_Sales_Model_Quote */ $quote = Mage::getModel("sales/quote"); - if (!(is_string($store) && is_integer($store))) { + if (!(is_string($store) || is_integer($store))) { $quote->loadByIdWithoutStore($quoteId); } else { $storeId = $this->_getStoreId($store); diff --git a/app/code/core/Mage/Checkout/Model/Api/Resource/Customer.php b/app/code/core/Mage/Checkout/Model/Api/Resource/Customer.php index b446a0fbc0..670b188d4d 100644 --- a/app/code/core/Mage/Checkout/Model/Api/Resource/Customer.php +++ b/app/code/core/Mage/Checkout/Model/Api/Resource/Customer.php @@ -203,9 +203,9 @@ public function involveNewCustomer(Mage_Sales_Model_Quote $quote) { $customer = $quote->getCustomer(); if ($customer->isConfirmationRequired()) { - $customer->sendNewAccountEmail('confirmation', '', $quote->getStoreId()); + $customer->sendNewAccountEmail('confirmation'); } else { - $customer->sendNewAccountEmail('registered' , '', $quote->getStoreId()); + $customer->sendNewAccountEmail(); } return $this; diff --git a/app/code/core/Mage/Checkout/Model/Resource/Agreement/Collection.php b/app/code/core/Mage/Checkout/Model/Resource/Agreement/Collection.php index fb555b78f8..6dbc11fe7b 100755 --- a/app/code/core/Mage/Checkout/Model/Resource/Agreement/Collection.php +++ b/app/code/core/Mage/Checkout/Model/Resource/Agreement/Collection.php @@ -34,6 +34,10 @@ */ class Mage_Checkout_Model_Resource_Agreement_Collection extends Mage_Core_Model_Resource_Db_Collection_Abstract { + protected $_map = array('fields' => array( + 'agreement_id' => 'main_table.agreement_id', + )); + /** * Is store filter with admin store * diff --git a/app/code/core/Mage/Checkout/Model/Type/Multishipping.php b/app/code/core/Mage/Checkout/Model/Type/Multishipping.php index c0e19702c0..93edf95e48 100644 --- a/app/code/core/Mage/Checkout/Model/Type/Multishipping.php +++ b/app/code/core/Mage/Checkout/Model/Type/Multishipping.php @@ -470,6 +470,12 @@ protected function _validate() Mage::throwException($helper->__('Invalid checkout type.')); } + /** @var $paymentMethod Mage_Payment_Model_Method_Abstract */ + $paymentMethod = $quote->getPayment()->getMethodInstance(); + if (!empty($paymentMethod) && !$paymentMethod->isAvailable($quote)) { + Mage::throwException($helper->__('Please specify payment method.')); + } + $addresses = $quote->getAllShippingAddresses(); foreach ($addresses as $address) { $addressValidation = $address->validate(); diff --git a/app/code/core/Mage/Checkout/etc/api.xml b/app/code/core/Mage/Checkout/etc/api.xml index 0c582315a3..b22730f7ed 100644 --- a/app/code/core/Mage/Checkout/etc/api.xml +++ b/app/code/core/Mage/Checkout/etc/api.xml @@ -381,6 +381,46 @@ + + cart_customer + + + + + + add + + + remove + + + + + method + + + + + method + + + + + set + + + + + addresses + + + + + method + + + + diff --git a/app/code/core/Mage/Core/Model/Store/Api.php b/app/code/core/Mage/Core/Model/Store/Api.php new file mode 100644 index 0000000000..3271c070ad --- /dev/null +++ b/app/code/core/Mage/Core/Model/Store/Api.php @@ -0,0 +1,96 @@ + + */ + +class Mage_Core_Model_Store_Api extends Mage_Api_Model_Resource_Abstract +{ + /** + * Retrieve stores list + * + * @return array + */ + public function items() + { + // Retrieve stores + $stores = Mage::app()->getStores(); + + // Make result array + $result = array(); + foreach ($stores as $store) { + $result[] = array( + 'store_id' => $store->getId(), + 'code' => $store->getCode(), + 'website_id' => $store->getWebsiteId(), + 'group_id' => $store->getGroupId(), + 'name' => $store->getName(), + 'sort_order' => $store->getSortOrder(), + 'is_active' => $store->getIsActive() + ); + } + + return $result; + } + + /** + * Retrieve store data + * + * @param string|int $storeId + * @return array + */ + public function info($storeId) + { + // Retrieve store info + try { + $store = Mage::app()->getStore($storeId); + } catch (Mage_Core_Model_Store_Exception $e) { + $this->_fault('store_not_exists'); + } + + if (!$store->getId()) { + $this->_fault('store_not_exists'); + } + + // Basic store data + $result = array(); + $result['store_id'] = $store->getId(); + $result['code'] = $store->getCode(); + $result['website_id'] = $store->getWebsiteId(); + $result['group_id'] = $store->getGroupId(); + $result['name'] = $store->getName(); + $result['sort_order'] = $store->getSortOrder(); + $result['is_active'] = $store->getIsActive(); + + return $result; + } + +} diff --git a/app/code/core/Mage/Core/Model/Store/Api/V2.php b/app/code/core/Mage/Core/Model/Store/Api/V2.php new file mode 100644 index 0000000000..1e08e5ca00 --- /dev/null +++ b/app/code/core/Mage/Core/Model/Store/Api/V2.php @@ -0,0 +1,36 @@ + + */ +class Mage_Core_Model_Store_Api_V2 extends Mage_Core_Model_Store_Api +{ +} diff --git a/app/code/core/Mage/Core/etc/api.xml b/app/code/core/Mage/Core/etc/api.xml new file mode 100644 index 0000000000..4e728d23d7 --- /dev/null +++ b/app/code/core/Mage/Core/etc/api.xml @@ -0,0 +1,84 @@ + + + + + + + core/store_api + Store API + core/store + + + Retrieve store list + items + core/store/list + + + Retrieve store data + core/store/info + + + + + 100 + Requested store view not found. + + + + + + core_store + + + + store + + + + + + + + + + Core + 1 + + Store + + Retrieve store data + + + List of stores + + + + + + + diff --git a/app/code/core/Mage/Core/etc/wsdl.xml b/app/code/core/Mage/Core/etc/wsdl.xml new file mode 100644 index 0000000000..0689e69f79 --- /dev/null +++ b/app/code/core/Mage/Core/etc/wsdl.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List of stores + + + + + Store view info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Core/etc/wsi.xml b/app/code/core/Mage/Core/etc/wsi.xml new file mode 100644 index 0000000000..8877c0fbdb --- /dev/null +++ b/app/code/core/Mage/Core/etc/wsi.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + List of stores + + + + + Store view info + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Downloadable/Model/Link/Api.php b/app/code/core/Mage/Downloadable/Model/Link/Api.php new file mode 100644 index 0000000000..96a30973c1 --- /dev/null +++ b/app/code/core/Mage/Downloadable/Model/Link/Api.php @@ -0,0 +1,276 @@ + + */ +class Mage_Downloadable_Model_Link_Api extends Mage_Catalog_Model_Api_Resource +{ + /** + * Return validator instance + * + * @return Mage_Downloadable_Model_Link_Api_Validator + */ + protected function _getValidator() + { + return Mage::getSingleton('downloadable/link_api_validator'); + } + + /** + * Decode file from base64 and upload it to donwloadable 'tmp' folder + * + * @param array $fileInfo + * @param string $type + * @return string + */ + protected function _uploadFile($fileInfo, $type) + { + $tmpPath = ''; + if ($type == 'sample') { + $tmpPath = Mage_Downloadable_Model_Sample::getBaseTmpPath(); + } elseif ($type == 'link') { + $tmpPath = Mage_Downloadable_Model_Link::getBaseTmpPath(); + } elseif ($type == 'link_samples') { + $tmpPath = Mage_Downloadable_Model_Link::getBaseSampleTmpPath(); + } + + $result = array(); + try { + $uploader = Mage::getModel('downloadable/link_api_uploader', $fileInfo); + $uploader->setAllowRenameFiles(true); + $uploader->setFilesDispersion(true); + $result = $uploader->save($tmpPath); + + if (isset($result['file'])) { + $fullPath = rtrim($tmpPath, DS) . DS . ltrim($result['file'], DS); + Mage::helper('core/file_storage_database')->saveFile($fullPath); + } + } catch (Exception $e) { + if ($e->getMessage() != '') { + $this->_fault('upload_failed', $e->getMessage()); + } else { + $this->_fault($e->getCode()); + } + } + + $result['status'] = 'new'; + $result['name'] = substr($result['file'], strrpos($result['file'], '/')+1); + return Mage::helper('core')->jsonEncode(array($result)); + } + + /** + * Add downloadable content to product + * + * @param int|string $productId + * @param array $resource + * @param string $resourceType + * @param string|int|null $store + * @param string|null $identifierType ('sku'|'id') + * @return boolean + */ + public function add($productId, $resource, $resourceType, $store = null, $identifierType = null) + { + try { + $this->_getValidator()->validateType($resourceType); + $this->_getValidator()->validateAttributes($resource, $resourceType); + } catch (Exception $e) { + $this->_fault('validation_error', $e->getMessage()); + } + + $resource['is_delete'] = 0; + if ($resourceType == 'link') { + $resource['link_id'] = 0; + } elseif ($resourceType == 'sample') { + $resource['sample_id'] = 0; + } + + if ($resource['type'] == 'file') { + if (isset($resource['file'])) { + $resource['file'] = $this->_uploadFile($resource['file'], $resourceType); + } + unset($resource[$resourceType.'_url']); + } elseif ($resource['type'] == 'url') { + unset($resource['file']); + } + + if ($resourceType == 'link' && $resource['sample']['type'] == 'file') { + if (isset($resource['sample']['file'])) { + $resource['sample']['file'] = $this->_uploadFile($resource['sample']['file'], 'link_samples'); + } + unset($resource['sample']['url']); + } elseif ($resourceType == 'link' && $resource['sample']['type'] == 'url') { + $resource['sample']['file'] = null; + } + + $product = $this->_getProduct($productId, $store, $identifierType); + try { + $downloadable = array($resourceType => array($resource)); + $product->setDownloadableData($downloadable); + $product->save(); + } catch (Exception $e) { + $this->_fault('save_error', $e->getMessage()); + } + + return true; + } + + /** + * Retrieve downloadable product links + * + * @param int|string $productId + * @param string|int $store + * @param string $identifierType ('sku'|'id') + * @return array + */ + public function items($productId, $store = null, $identifierType = null) + { + $product = $this->_getProduct($productId, $store, $identifierType); + + $linkArr = array(); + $links = $product->getTypeInstance(true)->getLinks($product); + foreach ($links as $item) { + $tmpLinkItem = array( + 'link_id' => $item->getId(), + 'title' => $item->getTitle(), + 'price' => $item->getPrice(), + 'number_of_downloads' => $item->getNumberOfDownloads(), + 'is_shareable' => $item->getIsShareable(), + 'link_url' => $item->getLinkUrl(), + 'link_type' => $item->getLinkType(), + 'sample_file' => $item->getSampleFile(), + 'sample_url' => $item->getSampleUrl(), + 'sample_type' => $item->getSampleType(), + 'sort_order' => $item->getSortOrder() + ); + $file = Mage::helper('downloadable/file')->getFilePath( + Mage_Downloadable_Model_Link::getBasePath(), $item->getLinkFile() + ); + + if ($item->getLinkFile() && !is_file($file)) { + Mage::helper('core/file_storage_database')->saveFileToFilesystem($file); + } + + if ($item->getLinkFile() && is_file($file)) { + $name = Mage::helper('downloadable/file')->getFileFromPathFile($item->getLinkFile()); + $tmpLinkItem['file_save'] = array( + array( + 'file' => $item->getLinkFile(), + 'name' => $name, + 'size' => filesize($file), + 'status' => 'old' + )); + } + $sampleFile = Mage::helper('downloadable/file')->getFilePath( + Mage_Downloadable_Model_Link::getBaseSamplePath(), $item->getSampleFile() + ); + if ($item->getSampleFile() && is_file($sampleFile)) { + $tmpLinkItem['sample_file_save'] = array( + array( + 'file' => $item->getSampleFile(), + 'name' => Mage::helper('downloadable/file')->getFileFromPathFile($item->getSampleFile()), + 'size' => filesize($sampleFile), + 'status' => 'old' + )); + } + if ($item->getNumberOfDownloads() == '0') { + $tmpLinkItem['is_unlimited'] = 1; + } + if ($product->getStoreId() && $item->getStoreTitle()) { + $tmpLinkItem['store_title'] = $item->getStoreTitle(); + } + if ($product->getStoreId() && Mage::helper('downloadable')->getIsPriceWebsiteScope()) { + $tmpLinkItem['website_price'] = $item->getWebsitePrice(); + } + $linkArr[] = $tmpLinkItem; + } + unset($item); + unset($tmpLinkItem); + unset($links); + + $samples = $product->getTypeInstance(true)->getSamples($product)->getData(); + return array('links' => $linkArr, 'samples' => $samples); + } + + /** + * Remove downloadable product link + * @param string $linkId + * @param string $resourceType + * @return bool + */ + public function remove($linkId, $resourceType) + { + try { + $this->_getValidator()->validateType($resourceType); + } catch (Exception $e) { + $this->_fault('validation_error', $e->getMessage()); + } + + switch($resourceType) { + case 'link': + $downloadableModel = Mage::getSingleton('downloadable/link'); + break; + case 'sample': + $downloadableModel = Mage::getSingleton('downloadable/sample'); + break; + } + + $downloadableModel->load($linkId); + if (is_null($downloadableModel->getId())) { + $this->_fault('link_was_not_found'); + } + + try { + $downloadableModel->delete(); + } catch (Exception $e) { + $this->_fault('remove_error', $e->getMessage()); + } + + return true; + } + + /** + * Return loaded downloadable product instance + * + * @param int|string $productId (SKU or ID) + * @param int|string $store + * @param string $identifierType + * @return Mage_Catalog_Model_Product + */ + protected function _getProduct($productId, $store = null, $identifierType = null) + { + $product = parent::_getProduct($productId, $store, $identifierType); + + if ($product->getTypeId() !== Mage_Downloadable_Model_Product_Type::TYPE_DOWNLOADABLE) { + $this->_fault('product_not_downloadable'); + } + + return $product; + } +} diff --git a/app/code/core/Mage/Downloadable/Model/Link/Api/Uploader.php b/app/code/core/Mage/Downloadable/Model/Link/Api/Uploader.php new file mode 100644 index 0000000000..5751f8dac6 --- /dev/null +++ b/app/code/core/Mage/Downloadable/Model/Link/Api/Uploader.php @@ -0,0 +1,129 @@ + + */ +class Mage_Downloadable_Model_Link_Api_Uploader extends Mage_Core_Model_File_Uploader +{ + /** + * Filename prefix + * + * @var string + */ + protected $_filePrefix = 'Api'; + + /** + * Default file type + */ + const DEFAULT_FILE_TYPE = 'application/octet-stream'; + + /** + * Check if the uploaded file exists + * + * @throws Exception + * @param array $file + */ + public function __construct($file) + { + $this->_setUploadFile($file); + if( !file_exists($this->_file['tmp_name']) ) { + throw new Exception('', 'file_not_uploaded'); + } else { + $this->_fileExists = true; + } + } + + /** + * Sets uploaded file info and decodes the file + * + * @throws Exception + * @param array $fileInfo + * @return void + */ + private function _setUploadFile($fileInfo) + { + if (!is_array($fileInfo)) { + throw new Exception('', 'file_data_not_correct'); + } + + $this->_file = $this->_decodeFile($fileInfo); + $this->_uploadType = self::SINGLE_STYLE; + } + + /** + * Decode uploaded file base64 encoded content + * + * @param array $fileInfo + * @return array + */ + private function _decodeFile($fileInfo) + { + $tmpFileName = $this->_getTmpFilePath(); + + $file = new Varien_Io_File(); + $file->open(array('path' => sys_get_temp_dir())); + $file->streamOpen($tmpFileName); + $file->streamWrite(base64_decode($fileInfo['base64_content'])); + $file->streamClose(); + + return array( + 'name' => $fileInfo['name'], + 'type' => isset($fileInfo['type'])? $fileInfo['type'] : self::DEFAULT_FILE_TYPE, + 'tmp_name' => $tmpFileName, + 'error' => 0, + 'size' => filesize($tmpFileName) + ); + } + + /** + * Generate temporary file name + * + * @return string + */ + private function _getTmpFilePath() + { + return tempnam(sys_get_temp_dir(), $this->_filePrefix); + + } + + /** + * Moves a file + * + * @param string $sourceFile + * @param string $destinationFile + * @return bool + */ + protected function _moveFile($sourceFile, $destinationFile) + { + return rename($sourceFile, $destinationFile); + } + +} diff --git a/app/code/core/Mage/Downloadable/Model/Link/Api/V2.php b/app/code/core/Mage/Downloadable/Model/Link/Api/V2.php new file mode 100644 index 0000000000..7b9eb1dcbb --- /dev/null +++ b/app/code/core/Mage/Downloadable/Model/Link/Api/V2.php @@ -0,0 +1,67 @@ + + */ +class Mage_Downloadable_Model_Link_Api_V2 extends Mage_Downloadable_Model_Link_Api +{ + /** + * Clean the object, leave only property values + * + * @param object $var + * @return void + */ + protected function _prepareData(&$var) + { + if (is_object($var)) { + $var = get_object_vars($var); + foreach ($var as $key => &$value) { + $this->_prepareData($value); + } + } + } + + /** + * Add downloadable content to product + * + * @param int|string $productId + * @param object $resource + * @param string $resourceType + * @param string|int $store + * @param string $identifierType ('sku'|'id') + * @return type + */ + public function add($productId, $resource, $resourceType, $store = null, $identifierType = null) + { + $this->_prepareData($resource); + return parent::add($productId, $resource, $resourceType, $store, $identifierType); + } +} diff --git a/app/code/core/Mage/Downloadable/Model/Link/Api/Validator.php b/app/code/core/Mage/Downloadable/Model/Link/Api/Validator.php new file mode 100644 index 0000000000..3684a6d5bd --- /dev/null +++ b/app/code/core/Mage/Downloadable/Model/Link/Api/Validator.php @@ -0,0 +1,286 @@ + + */ +class Mage_Downloadable_Model_Link_Api_Validator //extends Mage_Api_Model_Resource_Abstract +{ + /** + * Acceptable resourceTypes array + * @var array + */ + protected $_types = array('link', 'sample'); + + /** + * Acceptable upload types array + * @var array + */ + protected $_uploadTypes = array('file', 'url'); + + /** + * List of all attributes and names endings of validation functions + * + * @var array + */ + protected $_defaultAttributes = array( + 'link' => array( + 'title' => 'Title', // $1 + 'price' => 'Price', // $2 + 'number_of_downloads' => 'NumOfDownloads', // if no set is_unlimited to 1 $3 + 'is_unlimited' => 'Unlimited', // 1|0 $4 + 'is_shareable' => 'Shareable', // 1|0|2 (2) $5 + 'type' => 'UploadType', // file|url (file) $6 + 'file' => 'File', // array(name, base64_content) $7 + 'link_url' => 'Url', // URL $8 + 'sort_order' => 'Order', // int (0) $9 + 'sample' => array( + 'type' => 'UploadType', // file|url (file) $6 + 'file' => 'File', // array(name, base64_content) $7 + 'url' => 'Url' // URL $8 + ) + ), + 'sample' => array( + 'title' => 'Title', // $1 + 'type' => 'UploadType', // file|url (file) $6 + 'file' => 'File', // array(name, base64_content) $7 + 'sample_url' => 'Url', // URL $8 + 'sort_order' => 'Order' // int (0) $9 + ) + ); + + /** + * Get resource types + * + * @return array + */ + public function getResourceTypes() + { + return $this->_types; + } + + /** + * Validate resourceType, it should be one of (links|samples|link_samples) + * + * @param string $type + * @return boolean + */ + public function validateType($type) + { + if (!in_array($type, $this->getResourceTypes())) { + throw new Exception('unknown_resource_type'); + } + return true; + } + + /** + * Validate all parameters and loads default values for omitted parameters. + * + * @param array $resource + * @param string $resourceType + */ + public function validateAttributes(&$resource, $resourceType) + { + $fields = $this->_defaultAttributes[$resourceType]; + $this->_dispatch($resource, $fields); + + $this->completeCheck($resource, $resourceType); + } + + /** + * Final check + * + * @param array $resource + * @param string $resourceType + */ + public function completeCheck(&$resource, $resourceType) + { + if ($resourceType == 'link') { + if ($resource['type'] == 'file') { + $this->validateFileDetails($resource['file']); + } + if ($resource['type'] == 'url' && empty($resource['link_url'])) { + throw new Exception('empty_url'); + } + // sample + if ($resource['sample']['type'] == 'file') { + $this->validateFileDetails($resource['sample']['file']); + } + if ($resource['sample']['type'] == 'url' && empty($resource['sample']['url'])) { + throw new Exception('empty_url'); + } + } + if ($resourceType == 'sample') { + if ($resource['type'] == 'file') { + $this->validateFileDetails($resource['file']); + } + if ($resource['type'] == 'url' && empty($resource['sample_url'])) { + throw new Exception('empty_url'); + } + } + } + + /** + * Validate variable, in case of fault throw exception + * + * @param mixed $var + */ + public function validateFileDetails(&$var) + { + if (!isset ($var['name']) || !is_string($var['name']) || strlen($var['name']) === 0) { + throw new Exception('no_filename'); + } + if (!isset ($var['base64_content']) + || !is_string($var['base64_content']) + || strlen($var['base64_content']) === 0 + ) { + throw new Exception('no_file_base64_content'); + } + } + + /** + * Runs all checks. + * + * @param array $resource + * @param array $fields + */ + protected function _dispatch(&$resource, $fields) + { + foreach ($fields as $name => $validator) { + if (is_string($validator) && strlen($validator) > 0 && array_key_exists($name, $resource)) { + $call = 'validate' . $validator; + $this->$call($resource[$name]); + } + if (is_array($validator)) { + $this->_dispatch($resource[$name], $validator); + } + } + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param string $var + */ + public function validateTitle(&$var) + { + if (!is_string($var) || strlen($var) === 0) { + throw new Exception('no_title'); + } + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param float $var + */ + public function validatePrice(&$var) + { + $var = is_numeric($var)? floatval($var) : floatval(0); + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param int $var + */ + public function validateNumOfDownloads(&$var) + { + $var = is_numeric($var)? intval($var) : 0; + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param int|boolean $var + */ + public function validateUnlimited(&$var) + { + $var = ((is_numeric($var) && $var >= 0 && $var <= 1) || (is_bool($var)))? intval($var) : 0; + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param int $var + */ + public function validateShareable(&$var) + { + $var = (is_numeric($var) && $var >= 0 && $var <= 2)? intval($var) : 2; + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param array $var + */ + public function validateFile(&$var) + { + $var = is_array($var)? $var : null; + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param string $var + */ + public function validateUrl(&$var) + { + + if (is_string($var) && strlen($var) > 0) { + $urlregex = "/^(https?|ftp)\:\/\/([a-z0-9+\!\*\(\)\,\;\?\&\=\$\_\.\-]+(\:[a-z0-9+\!\*\(\)\,\;\?\&\=\$\_\.\-]+)?@)?[a-z0-9\+\$\_\-]+(\.[a-z0-9+\$\_\-]+)*(\:[0-9]{2,5})?(\/([a-z0-9+\$\_\-]\.?)+)*\/?(\?[a-z\+\&\$\_\.\-][a-z0-9\;\:\@\/\&\%\=\+\$\_\.\-]*)?(#[a-z\_\.\-][a-z0-9\+\$\_\.\-]*)?$/i"; + if (!preg_match($urlregex, $var)) { + throw new Exception('url_not_valid'); + } + } else { + $var = ''; + } + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param int $var + */ + public function validateOrder(&$var) + { + $var = is_numeric($var)? intval($var) : 0; + } + + /** + * Validate variable, in case of fault loads default entity. + * + * @param string $var + */ + public function validateUploadType(&$var) + { + $var = in_array($var, $this->_uploadTypes)? $var : 'file'; + } +} diff --git a/app/code/core/Mage/Downloadable/Model/Resource/Indexer/Price.php b/app/code/core/Mage/Downloadable/Model/Resource/Indexer/Price.php index 25bd48402d..53cbc0ef08 100755 --- a/app/code/core/Mage/Downloadable/Model/Resource/Indexer/Price.php +++ b/app/code/core/Mage/Downloadable/Model/Resource/Indexer/Price.php @@ -156,7 +156,7 @@ protected function _applyDownloadableLink() $query = $select->crossUpdateFromSelect(array('i' => $this->_getDefaultFinalPriceTable())); $write->query($query); - if ($this->useIdxTable()) { + if ($this->useIdxTable() && $this->_allowTableChanges) { $write->truncateTable($table); } else { $write->delete($table); diff --git a/app/code/core/Mage/Downloadable/etc/api.xml b/app/code/core/Mage/Downloadable/etc/api.xml new file mode 100644 index 0000000000..21bb335751 --- /dev/null +++ b/app/code/core/Mage/Downloadable/etc/api.xml @@ -0,0 +1,156 @@ + + + + + + + downloadable/link_api + Category API + downloadable/link + + + Add links and samples to downloadable product + add + downloadable/link/add + + + Retrieve links and samples list from downloadable product + items + downloadable/link/list + + + Remove links and samples from downloadable product + downloadable/link/remove + + + + + 100 + Store with requested code/id does not exist. + + + 101 + Product with requested id does not exist. + + + 401 + Incorrect resourceType, only "links" and "samples" allowed. + + + 402 + URL cannot be empty. + + + 403 + Filename was omitted, or contains unallowed characters. + + + 404 + File content should be passed as base64 string. For details, please see http://en.wikipedia.org/wiki/Base64 + + + 405 + Title cannot be empty. + + + 406 + Incorrect content type only "link", "link_samples" and "sample" allowed. + + + 408 + Incorrect product type. Downloadable content, can be added only to "downloadable" products. + + + 409 + Incorrect extension of filename. + + + 410 + Uploaded file is too big. + + + 411 + Has no permissions for writing to temporary folder. + + + 411 + Cannot create sub folder in temporary folder. + + + 412 + Link or sample with specified ID was not found. + + + 413 + An allowed resource type values for remove method is: "links" and "samples". + + + 414 + Unable to save action. Details in error message. + + + 415 + Validation error has occurred. + + + 416 + Unable to remove link. Details in error message. + + + + + + catalog_product_downloadable_link + + + + catalogProductDownloadableLink + + + + + + + + Product downloadable links + + Add + + + List + + + Remove + + + + + + + + diff --git a/app/code/core/Mage/Downloadable/etc/wsdl.xml b/app/code/core/Mage/Downloadable/etc/wsdl.xml new file mode 100644 index 0000000000..b463be954b --- /dev/null +++ b/app/code/core/Mage/Downloadable/etc/wsdl.xml @@ -0,0 +1,193 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Add links to downloadable product + + + + + Retrieve list of links and samples for downloadable product + + + + + Remove links and samples from downloadable product + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Downloadable/etc/wsi.xml b/app/code/core/Mage/Downloadable/etc/wsi.xml new file mode 100644 index 0000000000..498eb427f3 --- /dev/null +++ b/app/code/core/Mage/Downloadable/etc/wsi.xml @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Add links to downloadable product + + + + + Retrieve list of links and samples for downloadable product + + + + + Remove links and samples from downloadable product + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Eav/Model/Entity/Attribute.php b/app/code/core/Mage/Eav/Model/Entity/Attribute.php index f4f2ea3c9a..15c644687b 100644 --- a/app/code/core/Mage/Eav/Model/Entity/Attribute.php +++ b/app/code/core/Mage/Eav/Model/Entity/Attribute.php @@ -112,6 +112,25 @@ public function deleteEntity() return $this->_getResource()->deleteEntity($this); } + /** + * Load entity_attribute_id into $this by $this->attribute_set_id + * + * @return Mage_Core_Model_Abstract + */ + public function loadEntityAttributeIdBySet() + { + // load attributes collection filtered by attribute_id and attribute_set_id + $filteredAttributes = $this->getResourceCollection() + ->setAttributeSetFilter($this->getAttributeSetId()) + ->addFieldToFilter('entity_attribute.attribute_id', $this->getId()) + ->load(); + if (count($filteredAttributes) > 0) { + // getFirstItem() can be used as we can have one or zero records in the collection + $this->setEntityAttributeId($filteredAttributes->getFirstItem()->getEntityAttributeId()); + } + return $this; + } + /** * Prepare data for save * @@ -129,10 +148,12 @@ protected function _beforeSave() /** * Check for maximum attribute_code length */ - if(isset($this->_data['attribute_code']) && - !Zend_Validate::is($this->_data['attribute_code'], - 'StringLength', - array('max' => self::ATTRIBUTE_CODE_MAX_LENGTH)) + if (isset($this->_data['attribute_code']) && + !Zend_Validate::is( + $this->_data['attribute_code'], + 'StringLength', + array('max' => self::ATTRIBUTE_CODE_MAX_LENGTH) + ) ) { throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Maximum length of attribute code must be less then %s symbols', self::ATTRIBUTE_CODE_MAX_LENGTH)); } @@ -141,9 +162,8 @@ protected function _beforeSave() $hasDefaultValue = ((string)$defaultValue != ''); if ($this->getBackendType() == 'decimal' && $hasDefaultValue) { - if (!Zend_Locale_Format::isNumber($defaultValue, - array('locale' => Mage::app()->getLocale()->getLocaleCode())) - ) { + $locale = Mage::app()->getLocale()->getLocaleCode(); + if (!Zend_Locale_Format::isNumber($defaultValue, array('locale' => $locale))) { throw Mage::exception('Mage_Eav', Mage::helper('eav')->__('Invalid default decimal value')); } diff --git a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php index a9418d6ca3..05bf02c7d6 100644 --- a/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php +++ b/app/code/core/Mage/GoogleCheckout/Model/Api/Xml/Callback.php @@ -351,6 +351,16 @@ protected function _responseNewOrderNotification() /* @var $quote Mage_Sales_Model_Quote */ $quote = $this->_loadQuote(); $quote->setIsActive(true)->reserveOrderId(); + + Mage::dispatchEvent('googlecheckout_create_order_before', array('quote' => $quote)); + if ($quote->getErrorMessage()) { + $this->getGRequest()->SendCancelOrder($this->getGoogleOrderNumber(), + $this->__('Order creation error'), + $quote->getErrorMessage() + ); + return; + } + $storeId = $quote->getStoreId(); Mage::app()->setCurrentStore(Mage::app()->getStore($storeId)); @@ -436,6 +446,7 @@ protected function _responseNewOrderNotification() $order->place(); $order->save(); $order->sendNewOrderEmail(); + Mage::dispatchEvent('googlecheckout_save_order_after', array('order' => $order)); $quote->setIsActive(false)->save(); diff --git a/app/code/core/Mage/Index/Model/Indexer.php b/app/code/core/Mage/Index/Model/Indexer.php index 74a222b2c4..4ac94a0373 100644 --- a/app/code/core/Mage/Index/Model/Indexer.php +++ b/app/code/core/Mage/Index/Model/Indexer.php @@ -43,6 +43,13 @@ class Mage_Index_Model_Indexer */ protected $_lockFlag = false; + /** + * Whether table changes are allowed + * + * @var bool + */ + protected $_allowTableChanges = true; + /** * Class constructor. Initialize index processes based on configuration */ @@ -216,7 +223,22 @@ public function processEntityAction(Varien_Object $entity, $entityType, $eventTy * Index and save event just in case if some process matched it */ if ($event->getProcessIds()) { - $this->indexEvent($event); + $this->_changeKeyStatus(false); + /** @var $resourceModel Mage_Index_Model_Resource_Abstract */ + $resourceModel = Mage::getResourceModel('index/process'); + $resourceModel->beginTransaction(); + $this->_allowTableChanges = false; + try { + $this->indexEvent($event); + $resourceModel->commit(); + $this->_allowTableChanges = true; + $this->_changeKeyStatus(); + } catch (Exception $e) { + $resourceModel->rollBack(); + $this->_allowTableChanges = true; + $this->_changeKeyStatus(); + throw $e; + } $event->save(); } return $this; @@ -239,18 +261,75 @@ protected function _runAll($method, $args) if (in_array($code, $processed)) { continue; } + + if (!$this->_allowTableChanges && is_callable(array($process, 'setAllowTableChanges'))) { + $process->setAllowTableChanges(false); + } if ($process->getDepends()) { foreach ($process->getDepends() as $processCode) { $dependProcess = $this->getProcessByCode($processCode); if ($dependProcess && !in_array($processCode, $processed)) { + if (!$this->_allowTableChanges && is_callable(array($dependProcess, 'setAllowTableChanges'))) { + $dependProcess->setAllowTableChanges(false); + } call_user_func_array(array($dependProcess, $method), $args); + if (!$this->_allowTableChanges && is_callable(array($dependProcess, 'setAllowTableChanges'))) { + $dependProcess->setAllowTableChanges(true); + } $processed[] = $processCode; } } } call_user_func_array(array($process, $method), $args); + if (!$this->_allowTableChanges && is_callable(array($process, 'setAllowTableChanges'))) { + $process->setAllowTableChanges(true); + } + $processed[] = $code; } } + + /** + * Enable/Disable keys in index tables + * + * @return Mage_Index_Model_Indexer + */ + protected function _changeKeyStatus($enable = true) + { + $processed = array(); + foreach ($this->_processesCollection as $process) { + $code = $process->getIndexerCode(); + if (in_array($code, $processed)) { + continue; + } + + if ($process->getDepends()) { + foreach ($process->getDepends() as $processCode) { + $dependProcess = $this->getProcessByCode($processCode); + if ($dependProcess && !in_array($processCode, $processed)) { + if ($dependProcess instanceof Mage_Index_Model_Process) { + if ($enable) { + $dependProcess->enableIndexerKeys(); + } else { + $dependProcess->disableIndexerKeys(); + } + $processed[] = $processCode; + } + } + } + } + + if ($process instanceof Mage_Index_Model_Process) { + if ($enable) { + $process->enableIndexerKeys(); + } else { + $process->disableIndexerKeys(); + } + $processed[] = $code; + } + } + + return $this; + } } diff --git a/app/code/core/Mage/Index/Model/Indexer/Abstract.php b/app/code/core/Mage/Index/Model/Indexer/Abstract.php index c22f3a97f8..f17c98cf20 100644 --- a/app/code/core/Mage/Index/Model/Indexer/Abstract.php +++ b/app/code/core/Mage/Index/Model/Indexer/Abstract.php @@ -32,6 +32,13 @@ abstract class Mage_Index_Model_Indexer_Abstract extends Mage_Core_Model_Abstrac { protected $_matchedEntities = array(); + /** + * Whether table changes are allowed + * + * @var bool + */ + protected $_allowTableChanges = true; + /** * Get Indexer name * @@ -140,9 +147,68 @@ public function callEventHandler(Mage_Index_Model_Event $event) $method = $this->_camelize($event->getType()); } - if (method_exists($this->_getResource(), $method)) { - $this->_getResource()->$method($event); + $resourceModel = $this->_getResource(); + if (method_exists($resourceModel, $method)) { + if (!$this->_allowTableChanges && is_callable(array($resourceModel, 'setAllowTableChanges'))) { + $resourceModel->setAllowTableChanges(false); + } + $resourceModel->$method($event); + if (!$this->_allowTableChanges && is_callable(array($resourceModel, 'setAllowTableChanges'))) { + $resourceModel->setAllowTableChanges(true); + } } return $this; } + + /** + * Set whether table changes are allowed + * + * @param bool $value + * @return Mage_Index_Model_Indexer_Abstract + */ + public function setAllowTableChanges($value = true) + { + $this->_allowTableChanges = $value; + return $this; + } + + /** + * Disable resource table keys + * + * @return Mage_Index_Model_Indexer_Abstract + */ + public function disableKeys() + { + if (empty($this->_resourceName)) { + return $this; + } + + $resourceModel = $this->getResource(); + if ($resourceModel instanceof Mage_Index_Model_Resource_Abstract) { + $resourceModel->useDisableKeys(true); + $resourceModel->disableTableKeys(); + } + + return $this; + } + + /** + * Enable resource table keys + * + * @return Mage_Index_Model_Indexer_Abstract + */ + public function enableKeys() + { + if (empty($this->_resourceName)) { + return $this; + } + + $resourceModel = $this->getResource(); + if ($resourceModel instanceof Mage_Index_Model_Resource_Abstract) { + $resourceModel->useDisableKeys(true); + $resourceModel->enableTableKeys(); + } + + return $this; + } } diff --git a/app/code/core/Mage/Index/Model/Process.php b/app/code/core/Mage/Index/Model/Process.php index afe0407d90..b2e355b277 100644 --- a/app/code/core/Mage/Index/Model/Process.php +++ b/app/code/core/Mage/Index/Model/Process.php @@ -82,6 +82,13 @@ class Mage_Index_Model_Process extends Mage_Core_Model_Abstract protected $_isLocked = null; protected $_lockFile = null; + /** + * Whether table changes are allowed + * + * @var bool + */ + protected $_allowTableChanges = true; + /** * Initialize resource */ @@ -203,7 +210,16 @@ public function processEvent(Mage_Index_Model_Event $event) return $this; } $this->_setEventNamespace($event); + + $indexer = $this->getIndexer(); + if (!$this->_allowTableChanges && is_callable(array($indexer, 'setAllowTableChanges'))) { + $indexer->setAllowTableChanges(false); + } $this->getIndexer()->processEvent($event); + if (!$this->_allowTableChanges && is_callable(array($indexer, 'setAllowTableChanges'))) { + $indexer->setAllowTableChanges(true); + } + $event->resetData(); $this->_resetEventNamespace($event); $event->addProcessId($this->getId(), self::EVENT_STATUS_DONE); @@ -459,4 +475,44 @@ public function getDepends() return $depends; } + + /** + * Set whether table changes are allowed + * + * @param bool $value + * @return Mage_Index_Model_Process + */ + public function setAllowTableChanges($value = true) + { + $this->_allowTableChanges = $value; + return $this; + } + + /** + * Disable keys in index table + * + * @return Mage_Index_Model_Process + */ + public function disableIndexerKeys() + { + $indexer = $this->getIndexer(); + if ($indexer) { + $indexer->disableKeys(); + } + return $this; + } + + /** + * Enable keys in index table + * + * @return Mage_Index_Model_Process + */ + public function enableIndexerKeys() + { + $indexer = $this->getIndexer(); + if ($indexer) { + $indexer->enableKeys(); + } + return $this; + } } diff --git a/app/code/core/Mage/Index/Model/Resource/Abstract.php b/app/code/core/Mage/Index/Model/Resource/Abstract.php index 45c0a71ce5..0484f929fe 100755 --- a/app/code/core/Mage/Index/Model/Resource/Abstract.php +++ b/app/code/core/Mage/Index/Model/Resource/Abstract.php @@ -50,6 +50,13 @@ abstract class Mage_Index_Model_Resource_Abstract extends Mage_Core_Model_Resour */ protected $_isDisableKeys = true; + /** + * Whether table changes are allowed + * + * @var bool + */ + protected $_allowTableChanges = true; + /** * Reindex all * @@ -161,7 +168,7 @@ public function insertFromSelect($select, $destTable, array $columns, $readToInd $to = $this->_getWriteAdapter(); } - if ($this->useDisableKeys()) { + if ($this->useDisableKeys() && $this->_allowTableChanges) { $to->disableTableKeys($destTable); } if ($from === $to) { @@ -184,7 +191,7 @@ public function insertFromSelect($select, $destTable, array $columns, $readToInd $to->insertArray($destTable, $columns, $data); } } - if ($this->useDisableKeys()) { + if ($this->useDisableKeys() && $this->_allowTableChanges) { $to->enableTableKeys($destTable); } return $this; @@ -226,4 +233,42 @@ public function clearTemporaryIndexTable() { $this->_getWriteAdapter()->delete($this->getIdxTable()); } + + /** + * Set whether table changes are allowed + * + * @param bool $value + * @return Mage_Index_Model_Resource_Abstract + */ + public function setAllowTableChanges($value = true) + { + $this->_allowTableChanges = $value; + return $this; + } + + /** + * Disable Main Table keys + * + * @return Mage_Index_Model_Resource_Abstract + */ + public function disableTableKeys() + { + if ($this->useDisableKeys()) { + $this->_getWriteAdapter()->disableTableKeys($this->getMainTable()); + } + return $this; + } + + /** + * Enable Main Table keys + * + * @return Mage_Index_Model_Resource_Abstract + */ + public function enableTableKeys() + { + if ($this->useDisableKeys()) { + $this->_getWriteAdapter()->enableTableKeys($this->getMainTable()); + } + return $this; + } } diff --git a/app/code/core/Mage/Paygate/Model/Authorizenet.php b/app/code/core/Mage/Paygate/Model/Authorizenet.php index 00ea5f259e..76783007b8 100644 --- a/app/code/core/Mage/Paygate/Model/Authorizenet.php +++ b/app/code/core/Mage/Paygate/Model/Authorizenet.php @@ -1132,7 +1132,7 @@ protected function _buildRequest(Varien_Object $payment) break; case self::REQUEST_TYPE_CREDIT: /** - * need to send last 4 digit credit card number to authorize.net + * Send last 4 digits of credit card number to authorize.net * otherwise it will give an error */ $request->setXCardNum($payment->getCcLast4()); @@ -1167,7 +1167,7 @@ protected function _buildRequest(Varien_Object $payment) ->setXCountry($billing->getCountry()) ->setXPhone($billing->getTelephone()) ->setXFax($billing->getFax()) - ->setXCustId($billing->getCustomerId()) + ->setXCustId($order->getCustomerId()) ->setXCustomerIp($order->getRemoteIp()) ->setXCustomerTaxId($billing->getTaxId()) ->setXEmail($order->getCustomerEmail()) diff --git a/app/code/core/Mage/Review/Model/Resource/Review.php b/app/code/core/Mage/Review/Model/Resource/Review.php index d39b8b782b..7203a60111 100755 --- a/app/code/core/Mage/Review/Model/Resource/Review.php +++ b/app/code/core/Mage/Review/Model/Resource/Review.php @@ -242,10 +242,10 @@ protected function _beforeDelete(Mage_Core_Model_Abstract $object) /** * Perform actions after object delete * - * @param Varien_Object $object + * @param Mage_Core_Model_Abstract $object * @return Mage_Review_Model_Resource_Review */ - protected function _afterDelete(Mage_Core_Model_Abstract $object) + public function afterDeleteCommit(Mage_Core_Model_Abstract $object) { $this->aggregate($object); @@ -313,7 +313,11 @@ public function aggregate($object) $ratingSummary = $ratingSummaryObject->getSum(); } - $reviewsCount = $this->getTotalReviews($object->getEntityPkValue(), true, $ratingSummaryObject->getStoreId()); + $reviewsCount = $this->getTotalReviews( + $object->getEntityPkValue(), + true, + $ratingSummaryObject->getStoreId() + ); $select = $readAdapter->select() ->from($this->_aggregateTable) ->where('entity_pk_value = :pk_value') diff --git a/app/code/core/Mage/Review/Model/Review.php b/app/code/core/Mage/Review/Model/Review.php index 132343bcee..949a29f5d9 100644 --- a/app/code/core/Mage/Review/Model/Review.php +++ b/app/code/core/Mage/Review/Model/Review.php @@ -139,6 +139,17 @@ public function validate() return $errors; } + /** + * Perform actions after object delete + * + * @return Mage_Core_Model_Abstract + */ + protected function _afterDeleteCommit() + { + $this->getResource()->afterDeleteCommit($this); + return parent::_afterDeleteCommit(); + } + /** * Append review summary to product collection * diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo.php index dfd55cd296..b5fd93a6c0 100644 --- a/app/code/core/Mage/Sales/Model/Order/Creditmemo.php +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo.php @@ -880,4 +880,15 @@ protected function _beforeSave() return $this; } + + /** + * Get creditmemos collection filtered by $filter + * + * @param array|null $filter + * @return Mage_Sales_Model_Resource_Order_Creditmemo_Collection + */ + public function getFilteredCollectionItems($filter = null) + { + return $this->getResourceCollection()->getFiltered($filter); + } } diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Api.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Api.php new file mode 100644 index 0000000000..9fd46b4eb3 --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Api.php @@ -0,0 +1,278 @@ + + */ +class Mage_Sales_Model_Order_Creditmemo_Api extends Mage_Sales_Model_Api_Resource +{ + + /** + * Initialize attributes' mapping + */ + public function __construct() + { + $this->_attributesMap['creditmemo'] = array( + 'creditmemo_id' => 'entity_id' + ); + $this->_attributesMap['creditmemo_item'] = array( + 'item_id' => 'entity_id' + ); + $this->_attributesMap['creditmemo_comment'] = array( + 'comment_id' => 'entity_id' + ); + } + + /** + * Retrieve credit memos by filters + * + * @param array|null $filter + * @return array + */ + public function items($filter = null) + { + $filter = $this->_prepareListFilter($filter); + try { + $result = array(); + /** @var $creditmemoModel Mage_Sales_Model_Order_Creditmemo */ + $creditmemoModel = Mage::getModel('sales/order_creditmemo'); + // map field name entity_id to creditmemo_id + foreach ($creditmemoModel->getFilteredCollectionItems($filter) as $creditmemo) { + $result[] = $this->_getAttributes($creditmemo, 'creditmemo'); + } + } catch (Exception $e) { + $this->_fault('invalid_filter', $e->getMessage()); + } + return $result; + } + + /** + * Make filter of appropriate format for list method + * + * @param array|null $filter + * @return array|null + */ + protected function _prepareListFilter($filter = null) + { + // prepare filter, map field creditmemo_id to entity_id + if (is_array($filter)) { + foreach ($filter as $field => $value) { + if (isset($this->_attributesMap['creditmemo'][$field])) { + $filter[$this->_attributesMap['creditmemo'][$field]] = $value; + unset($filter[$field]); + } + } + } + return $filter; + } + + /** + * Retrieve credit memo information + * + * @param string $creditmemoIncrementId + * @return array + */ + public function info($creditmemoIncrementId) + { + $creditmemo = $this->_getCreditmemo($creditmemoIncrementId); + // get credit memo attributes with entity_id' => 'creditmemo_id' mapping + $result = $this->_getAttributes($creditmemo, 'creditmemo'); + $result['order_increment_id'] = $creditmemo->getOrder()->load($creditmemo->getOrderId())->getIncrementId(); + // items refunded + $result['items'] = array(); + foreach ($creditmemo->getAllItems() as $item) { + $result['items'][] = $this->_getAttributes($item, 'creditmemo_item'); + } + // credit memo comments + $result['comments'] = array(); + foreach ($creditmemo->getCommentsCollection() as $comment) { + $result['comments'][] = $this->_getAttributes($comment, 'creditmemo_comment'); + } + return $result; + } + + /** + * Create new credit memo for order + * + * @param string $orderIncrementId + * @param array $data array('qtys' => array('sku1' => qty1, ... , 'skuN' => qtyN), + * 'shipping_amount' => value, 'adjustment_positive' => value, 'adjustment_negative' => value) + * @param string|null $comment + * @param bool $notifyCustomer + * @param bool $includeComment + * @param string $refundToStoreCreditAmount + * @return string $creditmemoIncrementId + */ + public function create($orderIncrementId, $data = null, $comment = null, $notifyCustomer = false, + $includeComment = false, $refundToStoreCreditAmount = null) + { + /** @var $order Mage_Sales_Model_Order */ + $order = Mage::getModel('sales/order')->load($orderIncrementId, 'increment_id'); + if (!$order->getId()) { + $this->_fault('order_not_exists'); + } + if (!$order->canCreditmemo()) { + $this->_fault('cannot_create_creditmemo'); + } + $data = $this->_prepareCreateData($data); + + /** @var $service Mage_Sales_Model_Service_Order */ + $service = Mage::getModel('sales/service_order', $order); + /** @var $creditmemo Mage_Sales_Model_Order_Creditmemo */ + $creditmemo = $service->prepareCreditmemo($data); + + // refund to Store Credit + if ($refundToStoreCreditAmount) { + // check if refund to Store Credit is available + if ($order->getCustomerIsGuest()) { + $this->_fault('cannot_refund_to_storecredit'); + } + $refundToStoreCreditAmount = max( + 0, + min($creditmemo->getBaseCustomerBalanceReturnMax(), $refundToStoreCreditAmount) + ); + if ($refundToStoreCreditAmount) { + $refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice($refundToStoreCreditAmount); + $creditmemo->setBaseCustomerBalanceTotalRefunded($refundToStoreCreditAmount); + $refundToStoreCreditAmount = $creditmemo->getStore()->roundPrice( + $refundToStoreCreditAmount*$order->getStoreToOrderRate() + ); + // this field can be used by customer balance observer + $creditmemo->setBsCustomerBalTotalRefunded($refundToStoreCreditAmount); + // setting flag to make actual refund to customer balance after credit memo save + $creditmemo->setCustomerBalanceRefundFlag(true); + } + } + $creditmemo->setPaymentRefundDisallowed(true)->register(); + // add comment to creditmemo + if (!empty($comment)) { + $creditmemo->addComment($comment, $notifyCustomer); + } + try { + Mage::getModel('core/resource_transaction') + ->addObject($creditmemo) + ->addObject($order) + ->save(); + // send email notification + $creditmemo->sendEmail($notifyCustomer, ($includeComment ? $comment : '')); + } catch (Mage_Core_Exception $e) { + $this->_fault('data_invalid', $e->getMessage()); + } + return $creditmemo->getIncrementId(); + } + + /** + * Add comment to credit memo + * + * @param string $creditmemoIncrementId + * @param string $comment + * @param boolean $notifyCustomer + * @param boolean $includeComment + * @return boolean + */ + public function addComment($creditmemoIncrementId, $comment, $notifyCustomer = false, $includeComment = false) + { + $creditmemo = $this->_getCreditmemo($creditmemoIncrementId); + try { + $creditmemo->addComment($comment, $notifyCustomer)->save(); + $creditmemo->sendUpdateEmail($notifyCustomer, ($includeComment ? $comment : '')); + } catch (Mage_Core_Exception $e) { + $this->_fault('data_invalid', $e->getMessage()); + } + + return true; + } + + /** + * Cancel credit memo + * + * @param string $creditmemoIncrementId + * @return boolean + */ + public function cancel($creditmemoIncrementId) + { + $creditmemo = $this->_getCreditmemo($creditmemoIncrementId); + + if (!$creditmemo->canCancel()) { + $this->_fault('status_not_changed', Mage::helper('sales')->__('Credit memo cannot be canceled.')); + } + try { + $creditmemo->cancel()->save(); + } catch (Exception $e) { + $this->_fault('status_not_changed', Mage::helper('sales')->__('Credit memo canceling problem.')); + } + + return true; + } + + /** + * Hook method, could be replaced in derived classes + * + * @param array $data + * @return array + */ + protected function _prepareCreateData($data) + { + $data = isset($data) ? $data : array(); + + if (isset($data['qtys']) && count($data['qtys'])) { + $qtysArray = array(); + foreach ($data['qtys'] as $qKey => $qVal) { + // Save backward compatibility + if (is_array($qVal)) { + if (isset($qVal['order_item_id']) && isset($qVal['qty'])) { + $qtysArray[$qVal['order_item_id']] = $qVal['qty']; + } + } else { + $qtysArray[$qKey] = $qVal; + } + } + $data['qtys'] = $qtysArray; + } + return $data; + } + + /** + * Load CreditMemo by IncrementId + * + * @param mixed $incrementId + * @return Mage_Core_Model_Abstract|Mage_Sales_Model_Order_Creditmemo + */ + protected function _getCreditmemo($incrementId) + { + /** @var $creditmemo Mage_Sales_Model_Order_Creditmemo */ + $creditmemo = Mage::getModel('sales/order_creditmemo')->load($incrementId, 'increment_id'); + if (!$creditmemo->getId()) { + $this->_fault('not_exists'); + } + return $creditmemo; + } + +} diff --git a/app/code/core/Mage/Sales/Model/Order/Creditmemo/Api/V2.php b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Api/V2.php new file mode 100644 index 0000000000..1af1ffc6a1 --- /dev/null +++ b/app/code/core/Mage/Sales/Model/Order/Creditmemo/Api/V2.php @@ -0,0 +1,90 @@ + + */ +class Mage_Sales_Model_Order_Creditmemo_Api_V2 extends Mage_Sales_Model_Order_Creditmemo_Api +{ + + /** + * Prepare filters + * + * @param null|object $filters + * @return array + */ + protected function _prepareListFilter($filters = null) + { + $preparedFilters = array(); + $helper = Mage::helper('api'); + if (isset($filters->filter)) { + $helper->associativeArrayUnpack($filters->filter); + $preparedFilters += $filters->filter; + } + if (isset($filters->complex_filter)) { + $helper->associativeArrayUnpack($filters->complex_filter); + foreach ($filters->complex_filter as &$filter) { + $helper->associativeArrayUnpack($filter); + } + $preparedFilters += $filters->complex_filter; + } + foreach ($preparedFilters as $field => $value) { + if (isset($this->_attributesMap['creditmemo'][$field])) { + $preparedFilters[$this->_attributesMap['creditmemo'][$field]] = $value; + unset($preparedFilters[$field]); + } + } + + return $preparedFilters; + } + + /** + * Prepare data + * + * @param null|object $data + * @return array + */ + protected function _prepareCreateData($data) + { + // convert data object to array, if it's null turn it into empty array + $data = (isset($data) and is_object($data)) ? get_object_vars($data) : array(); + // convert qtys object to array + if (isset($data['qtys']) && count($data['qtys'])) { + $qtysArray = array(); + foreach ($data['qtys'] as &$item) { + if (isset($item->order_item_id) && isset($item->qty)) { + $qtysArray[$item->order_item_id] = $item->qty; + } + } + $data['qtys'] = $qtysArray; + } + return $data; + } +} diff --git a/app/code/core/Mage/Sales/Model/Quote.php b/app/code/core/Mage/Sales/Model/Quote.php index 5082549cea..11461cc4c9 100644 --- a/app/code/core/Mage/Sales/Model/Quote.php +++ b/app/code/core/Mage/Sales/Model/Quote.php @@ -1531,25 +1531,36 @@ public function validateMinimumAmount($multishipping = false) $storeId = $this->getStoreId(); $minOrderActive = Mage::getStoreConfigFlag('sales/minimum_order/active', $storeId); $minOrderMulti = Mage::getStoreConfigFlag('sales/minimum_order/multi_address', $storeId); + $minAmount = Mage::getStoreConfig('sales/minimum_order/amount', $storeId); if (!$minOrderActive) { return true; } + $addresses = $this->getAllAddresses(); + if ($multishipping) { if ($minOrderMulti) { + foreach ($addresses as $address) { + foreach ($address->getQuote()->getItemsCollection() as $item) { + $amount = $item->getBaseRowTotal() - $item->getBaseDiscountAmount(); + if ($amount < $minAmount) { + return false; + } + } + } + } else { $baseTotal = 0; - foreach ($this->getAllAddresses() as $address) { + foreach ($addresses as $address) { /* @var $address Mage_Sales_Model_Quote_Address */ $baseTotal += $address->getBaseSubtotalWithDiscount(); } - - if ($baseTotal < Mage::getStoreConfig('sales/minimum_order/amount', $storeId)) { + if ($baseTotal < $minAmount) { return false; } } } else { - foreach ($this->getAllAddresses() as $address) { + foreach ($addresses as $address) { /* @var $address Mage_Sales_Model_Quote_Address */ if (!$address->validateMinimumAmount()) { return false; diff --git a/app/code/core/Mage/Sales/Model/Resource/Order/Creditmemo/Collection.php b/app/code/core/Mage/Sales/Model/Resource/Order/Creditmemo/Collection.php index f13bd467c3..0edad5411e 100755 --- a/app/code/core/Mage/Sales/Model/Resource/Order/Creditmemo/Collection.php +++ b/app/code/core/Mage/Sales/Model/Resource/Order/Creditmemo/Collection.php @@ -74,4 +74,20 @@ protected function _afterLoad() $this->walk('afterLoad'); return $this; } + + /** + * Add filtration conditions + * + * @param array|null $filter + * @return Mage_Sales_Model_Resource_Order_Creditmemo_Collection + */ + public function getFiltered($filter = null) + { + if (is_array($filter)) { + foreach ($filter as $field => $value) { + $this->addFieldToFilter($field, $value); + } + } + return $this; + } } diff --git a/app/code/core/Mage/Sales/Model/Service/Order.php b/app/code/core/Mage/Sales/Model/Service/Order.php index f1a17f132a..88a5935661 100644 --- a/app/code/core/Mage/Sales/Model/Service/Order.php +++ b/app/code/core/Mage/Sales/Model/Service/Order.php @@ -87,7 +87,7 @@ public function prepareInvoice($qtys = array()) $invoice = $this->_convertor->toInvoice($this->_order); $totalQty = 0; foreach ($this->_order->getAllItems() as $orderItem) { - if (!$this->_canInvoiceItem($orderItem, $qtys)) { + if (!$this->_canInvoiceItem($orderItem, array())) { continue; } $item = $this->_convertor->itemToInvoiceItem($orderItem); diff --git a/app/code/core/Mage/Sales/etc/api.xml b/app/code/core/Mage/Sales/etc/api.xml index 7beb6c37fa..d01713a76c 100644 --- a/app/code/core/Mage/Sales/etc/api.xml +++ b/app/code/core/Mage/Sales/etc/api.xml @@ -179,10 +179,10 @@ 100 Requested invoice does not exist. - + 101 - Invalid filters given. Details in error message. - + Invalid filter given. Details in error message. + 102 Invalid data given. Details in error message. @@ -196,20 +196,103 @@ Invoice status not changed - + + + Credit memo API + sales/order_creditmemo_api + sales/order/creditmemo + + + Retrieve list of credit memos by filters + items + sales/order/creditmemo/list + + + Retrieve credit memo information + sales/order/creditmemo/info + + + Create new credit memo for order + sales/order/creditmemo/create + + + Add new comment to credit memo + sales/order/creditmemo/comment + + + Cancel credit memo + sales/order/creditmemo/cancel + + + + + 100 + Requested credit memo does not exist + + + 101 + Invalid filter given. Details in error message + + + 102 + Invalid data given. Details in error message + + + 103 + Requested order does not exist + + + 104 + Credit memo status not changed + + + 105 + Money can not be refunded to the store credit account as order was created by guest + + + 106 + Credit memo for requested order can not be created. + + + sales_order sales_order_shipment sales_order_invoice + sales_order_creditmemo salesOrder salesOrderShipment salesOrderInvoice + salesOrderCreditmemo + + + + + cancel + + + cart + order + + + + + cancel + + + + + cancel + + + + @@ -259,6 +342,24 @@ Retrieve invoice info + + Order credit memo + + Create + + + Comments + + + Cancel + + + Retrieve credit memo info + + + Retrieve credit memo list + + diff --git a/app/code/core/Mage/Sales/etc/wsdl.xml b/app/code/core/Mage/Sales/etc/wsdl.xml index 2ed242078e..9c14c2e519 100644 --- a/app/code/core/Mage/Sales/etc/wsdl.xml +++ b/app/code/core/Mage/Sales/etc/wsdl.xml @@ -603,6 +603,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -627,28 +779,28 @@ - + - + - + - + @@ -673,7 +825,7 @@ - + @@ -683,7 +835,7 @@ - + @@ -701,7 +853,7 @@ - + @@ -733,7 +885,7 @@ - + @@ -743,28 +895,71 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -867,6 +1062,31 @@ + + Retrieve list of creditmemos by filters + + + + + Retrieve creditmemo information + + + + + Create new creditmemo for order + + + + + Add new comment to shipment + + + + + Cancel creditmemo + + + @@ -1050,6 +1270,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Sales/etc/wsi.xml b/app/code/core/Mage/Sales/etc/wsi.xml index e48160047d..afb499ffe6 100644 --- a/app/code/core/Mage/Sales/etc/wsi.xml +++ b/app/code/core/Mage/Sales/etc/wsi.xml @@ -584,6 +584,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -906,6 +1052,89 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1028,6 +1257,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Retrieve list of orders by filters @@ -1129,6 +1388,31 @@ + + Retrieve list of creditmemos by filters + + + + + Retrieve creditmemo information + + + + + Create new creditmemo for order + + + + + Add new comment to creditmemo + + + + + Cancel creditmemo + + + @@ -1312,6 +1596,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Shipping/Model/Config.php b/app/code/core/Mage/Shipping/Model/Config.php index 4b81e4d998..f724d731ec 100644 --- a/app/code/core/Mage/Shipping/Model/Config.php +++ b/app/code/core/Mage/Shipping/Model/Config.php @@ -103,13 +103,8 @@ public function getCarrierInstance($carrierCode, $store = null) */ protected function _getCarrier($code, $config, $store = null) { -/* - if (isset(self::$_carriers[$code])) { - return self::$_carriers[$code]; - } -*/ if (!isset($config['model'])) { - throw Mage::exception('Mage_Shipping', 'Invalid model for shipping method: '.$code); + return false; } $modelName = $config['model']; diff --git a/app/code/core/Mage/Tag/Helper/Data.php b/app/code/core/Mage/Tag/Helper/Data.php index 423d299f1e..95307308b2 100644 --- a/app/code/core/Mage/Tag/Helper/Data.php +++ b/app/code/core/Mage/Tag/Helper/Data.php @@ -55,4 +55,34 @@ public function getStatusesOptionsArray() ) ); } + + /** + * Check tags on the correctness of symbols and split string to array of tags + * + * @param string $tagNamesInString + * @return array + */ + public function extractTags($tagNamesInString) + { + return explode("\n", preg_replace("/(\'(.*?)\')|(\s+)/i", "$1\n", $tagNamesInString)); + } + + /** + * Clear tag from the separating characters + * + * @param array $tagNamesArr + * @return array + */ + public function cleanTags(array $tagNamesArr) + { + foreach ($tagNamesArr as $key => $tagName) { + $tagNamesArr[$key] = trim($tagNamesArr[$key], '\''); + $tagNamesArr[$key] = trim($tagNamesArr[$key]); + if ($tagNamesArr[$key] == '') { + unset($tagNamesArr[$key]); + } + } + return $tagNamesArr; + } + } diff --git a/app/code/core/Mage/Tag/Model/Api.php b/app/code/core/Mage/Tag/Model/Api.php new file mode 100644 index 0000000000..e6a3fee04b --- /dev/null +++ b/app/code/core/Mage/Tag/Model/Api.php @@ -0,0 +1,244 @@ + + */ +class Mage_Tag_Model_Api extends Mage_Catalog_Model_Api_Resource +{ + /** + * Retrieve list of tags for specified product + * + * @param int $productId + * @param string|int $store + * @return array + */ + public function items($productId, $store = null) + { + $result = array(); + // fields list to return + $fieldsForResult = array('tag_id', 'name'); + + /** @var $product Mage_Catalog_Model_Product */ + $product = Mage::getModel('catalog/product')->load($productId); + if (!$product->getId()) { + $this->_fault('product_not_exists'); + } + + /** @var $tags Mage_Tag_Model_Resource_Tag_Collection */ + $tags = Mage::getModel('tag/tag')->getCollection()->joinRel()->addProductFilter($productId); + if ($store) { + $tags->addStoreFilter($this->_getStoreId($store)); + } + + /** @var $tag Mage_Tag_Model_Tag */ + foreach ($tags as $tag) { + $result[$tag->getId()] = $tag->toArray($fieldsForResult); + } + + return $result; + } + + /** + * Retrieve tag info as array('name'-> .., 'status' => .., + * 'base_popularity' => .., 'products' => array($productId => $popularity, ...)) + * + * @param int $tagId + * @param string|int $store + * @return array + */ + public function info($tagId, $store) + { + $result = array(); + $storeId = $this->_getStoreId($store); + /** @var $tag Mage_Tag_Model_Tag */ + $tag = Mage::getModel('tag/tag')->setStoreId($storeId)->setAddBasePopularity()->load($tagId); + if (!$tag->getId()) { + $this->_fault('tag_not_exists'); + } + $result['status'] = $tag->getStatus(); + $result['name'] = $tag->getName(); + $result['base_popularity'] = (is_numeric($tag->getBasePopularity())) ? $tag->getBasePopularity() : 0; + // retrieve array($productId => $popularity, ...) + $result['products'] = array(); + $relatedProductsCollection = $tag->getEntityCollection()->addTagFilter($tagId) + ->addStoreFilter($storeId)->addPopularity($tagId); + foreach ($relatedProductsCollection as $product) { + $result['products'][$product->getId()] = $product->getPopularity(); + } + + return $result; + } + + /** + * Add tag(s) to product. + * Return array of added/updated tags as array($tagName => $tagId, ...) + * + * @param array $data + * @return array + */ + public function add($data) + { + $data = $this->_prepareDataForAdd($data); + /** @var $product Mage_Catalog_Model_Product */ + $product = Mage::getModel('catalog/product')->load($data['product_id']); + if (!$product->getId()) { + $this->_fault('product_not_exists'); + } + /** @var $customer Mage_Customer_Model_Customer */ + $customer = Mage::getModel('customer/customer')->load($data['customer_id']); + if (!$customer->getId()) { + $this->_fault('customer_not_exists'); + } + $storeId = $this->_getStoreId($data['store']); + + try { + /** @var $tag Mage_Tag_Model_Tag */ + $tag = Mage::getModel('tag/tag'); + $tagNamesArr = Mage::helper('tag')->cleanTags(Mage::helper('tag')->extractTags($data['tag'])); + foreach ($tagNamesArr as $tagName) { + // unset previously added tag data + $tag->unsetData(); + $tag->loadByName($tagName); + if (!$tag->getId()) { + $tag->setName($tagName) + ->setFirstCustomerId($customer->getId()) + ->setFirstStoreId($storeId) + ->setStatus($tag->getPendingStatus()) + ->save(); + } + $tag->saveRelation($product->getId(), $customer->getId(), $storeId); + $result[$tagName] = $tag->getId(); + } + } catch (Mage_Core_Exception $e) { + $this->_fault('save_error', $e->getMessage()); + } + + return $result; + } + + /** + * Change existing tag information + * + * @param int $tagId + * @param array $data + * @param string|int $store + * @return bool + */ + public function update($tagId, $data, $store) + { + $data = $this->_prepareDataForUpdate($data); + $storeId = $this->_getStoreId($store); + /** @var $tag Mage_Tag_Model_Tag */ + $tag = Mage::getModel('tag/tag')->setStoreId($storeId)->setAddBasePopularity()->load($tagId); + if (!$tag->getId()) { + $this->_fault('tag_not_exists'); + } + + // store should be set for 'base_popularity' to be saved in Mage_Tag_Model_Resource_Tag::_afterSave() + $tag->setStore($storeId); + if (isset($data['base_popularity'])) { + $tag->setBasePopularity($data['base_popularity']); + } + if (isset($data['name'])) { + $tag->setName(trim($data['name'])); + } + if (isset($data['status'])) { + // validate tag status + if (!in_array($data['status'], array( + $tag->getApprovedStatus(), $tag->getPendingStatus(), $tag->getDisabledStatus()))) { + $this->_fault('invalid_data'); + } + $tag->setStatus($data['status']); + } + + try { + $tag->save(); + } catch (Mage_Core_Exception $e) { + $this->_fault('save_error', $e->getMessage()); + } + + return true; + } + + /** + * Remove existing tag + * + * @param int $tagId + * @return bool + */ + public function remove($tagId) + { + /** @var $tag Mage_Tag_Model_Tag */ + $tag = Mage::getModel('tag/tag')->load($tagId); + if (!$tag->getId()) { + $this->_fault('tag_not_exists'); + } + try { + $tag->delete(); + } catch (Mage_Core_Exception $e) { + $this->_fault('remove_error', $e->getMessage()); + } + + return true; + } + + /** + * Check data before add + * + * @param array $data + * @return array + */ + protected function _prepareDataForAdd($data) + { + if (!isset($data['product_id']) or !isset($data['tag']) + or !isset($data['customer_id']) or !isset($data['store'])) { + $this->_fault('invalid_data'); + } + + return $data; + } + + /** + * Check data before update + * + * @param $data + * @return + */ + protected function _prepareDataForUpdate($data) + { + // $data should contain at least one field to change + if ( !(isset($data['name']) or isset($data['status']) or isset($data['base_popularity']))) { + $this->_fault('invalid_data'); + } + + return $data; + } +} diff --git a/app/code/core/Mage/Tag/Model/Api/V2.php b/app/code/core/Mage/Tag/Model/Api/V2.php new file mode 100644 index 0000000000..a0bc9acc16 --- /dev/null +++ b/app/code/core/Mage/Tag/Model/Api/V2.php @@ -0,0 +1,109 @@ + + */ +class Mage_Tag_Model_Api_V2 extends Mage_Tag_Model_Api +{ + /** + * Retrieve list of tags for specified product as array of objects + * + * @param int $productId + * @param string|int $store + * @return array + */ + public function items($productId, $store) + { + $result = parent::items($productId, $store); + foreach ($result as $key => $tag) { + $result[$key] = Mage::helper('api')->wsiArrayPacker($tag); + } + return $result; + } + + /** + * Add tag(s) to product. + * Return array of objects + * + * @param array $data + * @return array + */ + public function add($data) + { + $result = array(); + foreach (parent::add($data) as $key => $value) { + $result[] = array('key' => $key, 'value' => $value); + } + + return $result; + } + + /** + * Retrieve tag info as object + * + * @param int $tagId + * @param string|int $store + * @return object + */ + public function info($tagId, $store) + { + $result = parent::info($tagId, $store); + $result = Mage::helper('api')->wsiArrayPacker($result); + foreach ($result->products as $key => $value) { + $result->products[$key] = array('key' => $key, 'value' => $value); + } + return $result; + } + + /** + * Convert data from object to array before add + * + * @param object $data + * @return array + */ + protected function _prepareDataForAdd($data) + { + Mage::helper('api')->toArray($data); + return parent::_prepareDataForAdd($data); + } + + /** + * Convert data from object to array before update + * + * @param object $data + * @return array + */ + protected function _prepareDataForUpdate($data) + { + Mage::helper('api')->toArray($data); + return parent::_prepareDataForUpdate($data); + } +} diff --git a/app/code/core/Mage/Tag/etc/api.xml b/app/code/core/Mage/Tag/etc/api.xml new file mode 100644 index 0000000000..3f64d1d415 --- /dev/null +++ b/app/code/core/Mage/Tag/etc/api.xml @@ -0,0 +1,138 @@ + + + + + + + Product Tag API + tag/api + catalog/product/tag + + + Retrieve list of tags by product + items + catalog/product/tag/list + + + Retrieve product tag info + catalog/product/tag/info + + + Add tag(s) to product + catalog/product/tag/add + + + Update product tag + catalog/product/tag/update + + + Remove product tag + catalog/product/tag/remove + + + + + 101 + Requested store does not exist. + + + 102 + Requested product does not exist. + + + 103 + Requested customer does not exist. + + + 104 + Requested tag does not exist. + + + 105 + Provided data is invalid. + + + 106 + Error while saving tag. Details in error message. + + + 107 + Error while removing tag. Details in error message. + + + + + + catalog_product_tag + + + + catalogProductTag + + + + + + + add + + + remove + + + + + + + + + + Tag + 103 + + List + + + Info + + + Add + + + Update + + + Remove + + + + + + + + diff --git a/app/code/core/Mage/Tag/etc/wsdl.xml b/app/code/core/Mage/Tag/etc/wsdl.xml new file mode 100644 index 0000000000..c7d114736c --- /dev/null +++ b/app/code/core/Mage/Tag/etc/wsdl.xml @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Retrieve list of tags by product + + + + + + + Retrieve product tag info + + + + + + + Add tag(s) to product + + + + + + + Update product tag + + + + + + + Remove product tag + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Tag/etc/wsi.xml b/app/code/core/Mage/Tag/etc/wsi.xml new file mode 100644 index 0000000000..615e2a4458 --- /dev/null +++ b/app/code/core/Mage/Tag/etc/wsi.xml @@ -0,0 +1,262 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Retrieve list of tags by product + + + + + + + Retrieve product tag info + + + + + + + Add tag(s) to product + + + + + + + Update product tag + + + + + + + Remove product tag + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Tax/Helper/Data.php b/app/code/core/Mage/Tax/Helper/Data.php index b72ace3b10..fc614c3db0 100644 --- a/app/code/core/Mage/Tax/Helper/Data.php +++ b/app/code/core/Mage/Tax/Helper/Data.php @@ -818,16 +818,22 @@ public function getCalculatedTaxes($source) foreach ($taxCollection as $tax) { $taxClassId = $tax['tax_id']; $percent = $tax['tax_percent']; + + $price = $item->getRowTotal(); + $basePrice = $item->getBaseRowTotal(); + if ($this->applyTaxAfterDiscount($item->getStoreId())) { + $price = $price - $item->getDiscountAmount() + $item->getHiddenTaxAmount(); + $basePrice = $basePrice - $item->getBaseDiscountAmount() + $item->getBaseHiddenTaxAmount(); + } + if (isset($taxClassAmount[$taxClassId])) { - $taxClassAmount[$taxClassId]['tax_amount'] += $item->getRowTotal() * $percent / 100; - $taxClassAmount[$taxClassId]['base_tax_amount'] += $item->getBaseRowTotal() * $percent / 100; - $taxClassAmount[$taxClassId]['hidden_tax_amount'] += $item->getHiddenTaxAmount(); + $taxClassAmount[$taxClassId]['tax_amount'] += $price * $percent / 100; + $taxClassAmount[$taxClassId]['base_tax_amount'] += $basePrice * $percent / 100; } else { - $taxClassAmount[$taxClassId]['tax_amount'] = $item->getRowTotal() * $percent / 100; - $taxClassAmount[$taxClassId]['base_tax_amount'] = $item->getBaseRowTotal() * $percent / 100; - $taxClassAmount[$taxClassId]['hidden_tax_amount'] = $item->getHiddenTaxAmount(); - $taxClassAmount[$taxClassId]['title'] = $tax['title']; - $taxClassAmount[$taxClassId]['percent'] = $tax['percent']; + $taxClassAmount[$taxClassId]['tax_amount'] = $price * $percent / 100; + $taxClassAmount[$taxClassId]['base_tax_amount'] = $basePrice * $percent / 100; + $taxClassAmount[$taxClassId]['title'] = $tax['title']; + $taxClassAmount[$taxClassId]['percent'] = $tax['percent']; } } } diff --git a/app/code/core/Mage/Tax/Model/Observer.php b/app/code/core/Mage/Tax/Model/Observer.php index d9dc2cc97a..e14d6d178f 100644 --- a/app/code/core/Mage/Tax/Model/Observer.php +++ b/app/code/core/Mage/Tax/Model/Observer.php @@ -68,12 +68,16 @@ public function salesEventOrderAfterSave(Varien_Event_Observer $observer) $taxes = $order->getAppliedTaxes(); $ratesIdQuoteItemId = array(); + if (!is_array($getTaxesForItems)) { + $getTaxesForItems = array(); + } foreach ($getTaxesForItems as $quoteItemId => $taxesArray) { foreach ($taxesArray as $rates) { if (count($rates['rates']) == 1) { $ratesIdQuoteItemId[$rates['id']][] = array( 'id' => $quoteItemId, - 'percent' => $rates['percent'] + 'percent' => $rates['percent'], + 'code' => $rates['rates'][0]['code'] ); } else { $percentDelta = $rates['percent']; @@ -81,25 +85,25 @@ public function salesEventOrderAfterSave(Varien_Event_Observer $observer) foreach ($rates['rates'] as $rate) { $ratesIdQuoteItemId[$rates['id']][] = array( 'id' => $quoteItemId, - 'percent' => $rate['percent'] + 'percent' => $rate['percent'], + 'code' => $rate['code'] ); $percentSum += $rate['percent']; } if ($percentDelta != $percentSum) { $delta = $percentDelta - $percentSum; - foreach ($ratesIdQuoteItemId[$rates['id']] as &$rate) { - $rate = array( - 'id' => $rate['id'], - 'percent' => (($rate['percent'] / $percentSum) * $delta) + $rate['percent'] - ); + foreach ($ratesIdQuoteItemId[$rates['id']] as &$rateTax) { + if ($rateTax['id'] == $quoteItemId) { + $rateTax['percent'] = (($rateTax['percent'] / $percentSum) * $delta) + + $rateTax['percent']; + } } } } } } - $a = array(); foreach ($taxes as $id => $row) { foreach ($row['rates'] as $tax) { if (is_null($row['percent'])) { @@ -127,22 +131,19 @@ public function salesEventOrderAfterSave(Varien_Event_Observer $observer) $result = Mage::getModel('tax/sales_order_tax')->setData($data)->save(); - if (isset($a[$id])) { - $a[$id] = $a[$id]+1; - } else { - $a[$id] = 0; - } - - if (isset($ratesIdQuoteItemId[$id]) && isset($ratesIdQuoteItemId[$id][$a[$id]])) { - $quoteItemId = $ratesIdQuoteItemId[$id][$a[$id]]; - $item = $order->getItemByQuoteItemId($quoteItemId['id']); - if ($item) { - $data = array( - 'item_id' => $item->getId(), - 'tax_id' => $result->getTaxId(), - 'tax_percent' => $quoteItemId['percent'] - ); - Mage::getModel('tax/sales_order_tax_item')->setData($data)->save(); + if (isset($ratesIdQuoteItemId[$id])) { + foreach ($ratesIdQuoteItemId[$id] as $quoteItemId) { + if ($quoteItemId['code'] == $tax['code']) { + $item = $order->getItemByQuoteItemId($quoteItemId['id']); + if ($item) { + $data = array( + 'item_id' => $item->getId(), + 'tax_id' => $result->getTaxId(), + 'tax_percent' => $quoteItemId['percent'] + ); + Mage::getModel('tax/sales_order_tax_item')->setData($data)->save(); + } + } } } } diff --git a/app/code/core/Mage/Tax/Model/Resource/Calculation.php b/app/code/core/Mage/Tax/Model/Resource/Calculation.php index 07b909a950..cb048adfed 100755 --- a/app/code/core/Mage/Tax/Model/Resource/Calculation.php +++ b/app/code/core/Mage/Tax/Model/Resource/Calculation.php @@ -314,7 +314,8 @@ protected function _getRates($request) $selectClone ->where('? BETWEEN rate.zip_from AND rate.zip_to', $postcode); } else if ($postcodeIsRange) { - $selectClone->where("rate.zip_from >= {$zipFrom} AND rate.zip_to <= {$zipTo}"); + $selectClone->where('rate.zip_from >= ?', $zipFrom) + ->where('rate.zip_to <= ?', $zipTo); } } diff --git a/app/code/core/Mage/Tax/Model/Sales/Total/Quote/Tax.php b/app/code/core/Mage/Tax/Model/Sales/Total/Quote/Tax.php index 29e785c99b..8e197ee9d8 100644 --- a/app/code/core/Mage/Tax/Model/Sales/Total/Quote/Tax.php +++ b/app/code/core/Mage/Tax/Model/Sales/Total/Quote/Tax.php @@ -296,6 +296,7 @@ protected function _calculateShippingTax(Mage_Sales_Model_Quote_Address $address protected function _unitBaseCalculation(Mage_Sales_Model_Quote_Address $address, $taxRateRequest) { $items = $this->_getAddressItems($address); + $itemTaxGroups = array(); foreach ($items as $item) { if ($item->getParentItem()) { continue; @@ -309,6 +310,9 @@ protected function _unitBaseCalculation(Mage_Sales_Model_Quote_Address $address, $this->_addAmount($child->getTaxAmount()); $this->_addBaseAmount($child->getBaseTaxAmount()); $applied = $this->_calculator->getAppliedRates($taxRateRequest); + if ($rate > 0) { + $itemTaxGroups[$child->getId()] = $applied; + } $this->_saveAppliedTaxes( $address, $applied, @@ -327,6 +331,9 @@ protected function _unitBaseCalculation(Mage_Sales_Model_Quote_Address $address, $this->_addAmount($item->getTaxAmount()); $this->_addBaseAmount($item->getBaseTaxAmount()); $applied = $this->_calculator->getAppliedRates($taxRateRequest); + if ($rate > 0) { + $itemTaxGroups[$item->getId()] = $applied; + } $this->_saveAppliedTaxes( $address, $applied, @@ -337,6 +344,10 @@ protected function _unitBaseCalculation(Mage_Sales_Model_Quote_Address $address, $item->setTaxRates($applied); } } + if ($address->getQuote()->getTaxesForItems()) { + $itemTaxGroups += $address->getQuote()->getTaxesForItems(); + } + $address->getQuote()->setTaxesForItems($itemTaxGroups); return $this; } @@ -420,6 +431,7 @@ protected function _calcUnitTaxAmount(Mage_Sales_Model_Quote_Item_Abstract $item protected function _rowBaseCalculation(Mage_Sales_Model_Quote_Address $address, $taxRateRequest) { $items = $this->_getAddressItems($address); + $itemTaxGroups = array(); foreach ($items as $item) { if ($item->getParentItem()) { continue; @@ -432,6 +444,9 @@ protected function _rowBaseCalculation(Mage_Sales_Model_Quote_Address $address, $this->_addAmount($child->getTaxAmount()); $this->_addBaseAmount($child->getBaseTaxAmount()); $applied = $this->_calculator->getAppliedRates($taxRateRequest); + if ($rate > 0) { + $itemTaxGroups[$child->getId()] = $applied; + } $this->_saveAppliedTaxes( $address, $applied, @@ -449,6 +464,9 @@ protected function _rowBaseCalculation(Mage_Sales_Model_Quote_Address $address, $this->_addAmount($item->getTaxAmount()); $this->_addBaseAmount($item->getBaseTaxAmount()); $applied = $this->_calculator->getAppliedRates($taxRateRequest); + if ($rate > 0) { + $itemTaxGroups[$item->getId()] = $applied; + } $this->_saveAppliedTaxes( $address, $applied, @@ -458,6 +476,11 @@ protected function _rowBaseCalculation(Mage_Sales_Model_Quote_Address $address, ); } } + + if ($address->getQuote()->getTaxesForItems()) { + $itemTaxGroups += $address->getQuote()->getTaxesForItems(); + } + $address->getQuote()->setTaxesForItems($itemTaxGroups); return $this; } @@ -552,19 +575,25 @@ protected function _totalBaseCalculation(Mage_Sales_Model_Quote_Address $address foreach ($item->getChildren() as $child) { $taxRateRequest->setProductClassId($child->getProduct()->getTaxClassId()); $rate = $this->_calculator->getRate($taxRateRequest); - $taxGroups[(string)$rate]['applied_rates'] = $this->_calculator->getAppliedRates($taxRateRequest); + $applied_rates = $this->_calculator->getAppliedRates($taxRateRequest); + $taxGroups[(string)$rate]['applied_rates'] = $applied_rates; $this->_aggregateTaxPerRate($child, $rate, $taxGroups); $inclTax = $child->getIsPriceInclTax(); - $itemTaxGroups[$child->getId()] = $this->_calculator->getAppliedRates($taxRateRequest); + if ($rate > 0) { + $itemTaxGroups[$child->getId()] = $applied_rates; + } } $this->_recalculateParent($item); } else { $taxRateRequest->setProductClassId($item->getProduct()->getTaxClassId()); $rate = $this->_calculator->getRate($taxRateRequest); - $taxGroups[(string)$rate]['applied_rates'] = $this->_calculator->getAppliedRates($taxRateRequest); + $applied_rates = $this->_calculator->getAppliedRates($taxRateRequest); + $taxGroups[(string)$rate]['applied_rates'] = $applied_rates; $this->_aggregateTaxPerRate($item, $rate, $taxGroups); $inclTax = $item->getIsPriceInclTax(); - $itemTaxGroups[$item->getId()] = $this->_calculator->getAppliedRates($taxRateRequest); + if ($rate > 0) { + $itemTaxGroups[$item->getId()] = $applied_rates; + } } } diff --git a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php index 2b9dbc73e0..d567b6e3c3 100644 --- a/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php +++ b/app/code/core/Mage/Usa/Model/Shipping/Carrier/Fedex.php @@ -85,6 +85,13 @@ class Mage_Usa_Model_Shipping_Carrier_Fedex */ protected $_shipServiceWsdl = null; + /** + * Path to wsdl file of track service + * + * @var string + */ + protected $_trackServiceWsdl = null; + /** * Container types that could be customized for FedEx carrier * @@ -95,27 +102,40 @@ class Mage_Usa_Model_Shipping_Carrier_Fedex public function __construct() { parent::__construct(); - $this->_shipServiceWsdl = Mage::getModuleDir('etc', 'Mage_Usa') . DS . 'wsdl' . DS . 'FedEx' - . DS . 'ShipService_v9.wsdl'; - $this->_rateServiceWsdl = Mage::getModuleDir('etc', 'Mage_Usa') . DS . 'wsdl' . DS . 'FedEx' - . DS . 'RateService_v9.wsdl'; + $wsdlBasePath = Mage::getModuleDir('etc', 'Mage_Usa') . DS . 'wsdl' . DS . 'FedEx' . DS; + $this->_shipServiceWsdl = $wsdlBasePath . 'ShipService_v9.wsdl'; + $this->_rateServiceWsdl = $wsdlBasePath . 'RateService_v9.wsdl'; + $this->_trackServiceWsdl = $wsdlBasePath . 'TrackService_v5.wsdl'; } /** - * Create rate soap client + * Create soap client with selected wsdl * + * @param string $wsdl + * @param bool|int $trace * @return SoapClient */ - protected function _createRateSoapClient() + protected function _createSoapClient($wsdl, $trace = false) { - $client = new SoapClient($this->_rateServiceWsdl); + $client = new SoapClient($wsdl, array('trace' => $trace)); $client->__setLocation($this->getConfigFlag('sandbox_mode') ? 'https://wsbeta.fedex.com:443/web-services/rate' : 'https://ws.fedex.com:443/web-services/rate' ); + return $client; } + /** + * Create rate soap client + * + * @return SoapClient + */ + protected function _createRateSoapClient() + { + return $this->_createSoapClient($this->_rateServiceWsdl); + } + /** * Create ship soap client * @@ -123,12 +143,17 @@ protected function _createRateSoapClient() */ protected function _createShipSoapClient() { - $client = new SoapClient($this->_shipServiceWsdl, array('trace' => 1)); - $client->__setLocation($this->getConfigFlag('sandbox_mode') - ? 'https://wsbeta.fedex.com:443/web-services/ship' - : 'https://ws.fedex.com:443/web-services/ship' - ); - return $client; + return $this->_createSoapClient($this->_shipServiceWsdl, 1); + } + + /** + * Create track soap client + * + * @return SoapClient + */ + protected function _createTrackSoapClient() + { + return $this->_createSoapClient($this->_trackServiceWsdl, 1); } /** @@ -823,58 +848,165 @@ protected function setTrackingReqeust() */ protected function _getXMLTracking($tracking) { - $r = $this->_rawTrackingRequest; - - $xml = new SimpleXMLElement(''); - $xml->addAttribute('xmlns:xsi', 'http://www.w3.org/2001/XMLSchema-instance'); - $xml->addAttribute('xsi:noNamespaceSchemaLocation', 'FDXTrack2Request.xsd'); + $trackRequest = array( + 'WebAuthenticationDetail' => array( + 'UserCredential' => array( + 'Key' => $this->getConfigData('key'), + 'Password' => $this->getConfigData('password') + ) + ), + 'ClientDetail' => array( + 'AccountNumber' => $this->getConfigData('account'), + 'MeterNumber' => $this->getConfigData('meter_number') + ), + 'Version' => array( + 'ServiceId' => 'trck', + 'Major' => '5', + 'Intermediate' => '0', + 'Minor' => '0' + ), + 'PackageIdentifier' => array( + 'Type' => 'TRACKING_NUMBER_OR_DOORTAG', + 'Value' => $tracking, + ), + /* + * 0 = summary data, one signle scan structure with the most recent scan + * 1 = multiple sacn activity for each package + */ + 'IncludeDetailedScans' => 1, + ); + $requestString = serialize($trackRequest); + $response = $this->_getCachedQuotes($requestString); + $debugData = array('request' => $trackRequest); + if ($response === null) { + try { + $client = $this->_createTrackSoapClient(); + $response = $client->track($trackRequest); + $this->_setCachedQuotes($requestString, serialize($response)); + $debugData['result'] = $response; + } catch (Exception $e) { + $debugData['result'] = array('error' => $e->getMessage(), 'code' => $e->getCode()); + Mage::logException($e); + } + } else { + $response = unserialize($response); + $debugData['result'] = $response; + } + $this->_debug($debugData); - $requestHeader = $xml->addChild('RequestHeader'); - $requestHeader->addChild('AccountNumber', $r->getAccount()); + $this->_parseTrackingResponse($tracking, $response); + } - /* - * for tracking result, actual meter number is not needed - */ - $requestHeader->addChild('MeterNumber', '0'); + /** + * Parse tracking response + * + * @param array $trackingValue + * @param stdClass $response + */ + protected function _parseTrackingResponse($trackingValue, $response) + { + if (is_object($response)) { + if ($response->HighestSeverity == 'FAILURE' || $response->HighestSeverity == 'ERROR') { + $errorTitle = (string)$response->Notifications->Message; + } elseif (isset($response->TrackDetails)) { + $trackInfo = $response->TrackDetails; + $resultArray['status'] = (string)$trackInfo->StatusDescription; + $resultArray['service'] = (string)$trackInfo->ServiceInfo; + $timestamp = isset($trackInfo->EstimatedDeliveryTimestamp) ? + $trackInfo->EstimatedDeliveryTimestamp : $trackInfo->ActualDeliveryTimestamp; + $timestamp = strtotime((string)$timestamp); + if ($timestamp) { + $resultArray['deliverydate'] = date('Y-m-d', $timestamp); + $resultArray['deliverytime'] = date('H:i:s', $timestamp); + } - $packageIdentifier = $xml->addChild('PackageIdentifier'); - $packageIdentifier->addChild('Value', $tracking); + $deliveryLocation = isset($trackInfo->EstimatedDeliveryAddress) ? + $trackInfo->EstimatedDeliveryAddress : $trackInfo->ActualDeliveryAddress; + $deliveryLocationArray = array(); + if (isset($deliveryLocation->City)) { + $deliveryLocationArray[] = (string)$deliveryLocation->City; + } + if (isset($deliveryLocation->StateOrProvinceCode)) { + $deliveryLocationArray[] = (string)$deliveryLocation->StateOrProvinceCode; + } + if (isset($deliveryLocation->CountryCode)) { + $deliveryLocationArray[] = (string)$deliveryLocation->CountryCode; + } + if ($deliveryLocationArray) { + $resultArray['deliverylocation'] = implode(', ', $deliveryLocationArray); + } - /* - * 0 = summary data, one signle scan structure with the most recent scan - * 1 = multiple sacn activity for each package - */ - $xml->addChild('DetailScans', '1'); + $resultArray['signedby'] = (string)$trackInfo->DeliverySignatureName; + $resultArray['shippeddate'] = date('Y-m-d', (int)$trackInfo->ShipTimestamp); + if (isset($trackInfo->PackageWeight) && isset($trackInfo->Units)) { + $weight = (string)$trackInfo->PackageWeight; + $unit = (string)$trackInfo->Units; + $resultArray['weight'] = "{$weight} {$unit}"; + } - $request = $xml->asXML(); - $debugData = array('request' => $request); + $packageProgress = array(); + if (isset($trackInfo->Events)) { + $events = $trackInfo->Events; + if (isset($events->Address)) { + $events = array($events); + } + foreach ($events as $event) { + $tempArray = array(); + $tempArray['activity'] = (string)$event->EventDescription; + $timestamp = strtotime((string)$event->Timestamp); + if ($timestamp) { + $tempArray['deliverydate'] = date('Y-m-d', $timestamp); + $tempArray['deliverytime'] = date('H:i:s', $timestamp); + } + if (isset($event->Address)) { + $addressArray = array(); + $address = $event->Address; + if (isset($address->City)) { + $addressArray[] = (string)$address->City; + } + if (isset($address->StateOrProvinceCode)) { + $addressArray[] = (string)$address->StateOrProvinceCode; + } + if (isset($address->CountryCode)) { + $addressArray[] = (string)$address->CountryCode; + } + if ($addressArray) { + $tempArray['deliverylocation'] = implode(', ', $addressArray); + } + } + $packageProgress[] = $tempArray; + } + } - try { - $url = $this->getConfigData('gateway_url'); - if (!$url) { - $url = $this->_defaultGatewayUrl; + $resultArray['progressdetail'] = $packageProgress; } - $ch = curl_init(); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); - curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); - curl_setopt($ch, CURLOPT_POSTFIELDS, $request); - $responseBody = curl_exec($ch); - $debugData['result'] = $responseBody; - curl_close ($ch); } - catch (Exception $e) { - $debugData['result'] = array('error' => $e->getMessage(), 'code' => $e->getCode()); - $responseBody = ''; + + if(!$this->_result){ + $this->_result = Mage::getModel('shipping/tracking_result'); + } + + if(isset($resultArray)) { + $tracking = Mage::getModel('shipping/tracking_result_status'); + $tracking->setCarrier('fedex'); + $tracking->setCarrierTitle($this->getConfigData('title')); + $tracking->setTracking($trackingValue); + $tracking->addData($resultArray); + $this->_result->append($tracking); + } else { + $error = Mage::getModel('shipping/tracking_result_error'); + $error->setCarrier('fedex'); + $error->setCarrierTitle($this->getConfigData('title')); + $error->setTracking($trackingValue); + $error->setErrorMessage($errorTitle ? $errorTitle : Mage::helper('usa')->__('Unable to retrieve tracking')); + $this->_result->append($error); } - $this->_debug($debugData); - $this->_parseXmlTrackingResponse($tracking, $responseBody); } /** * Parse xml tracking response * + * @deprecated after 1.6.0.0 see _parseTrackingResponse() * @param array $trackingvalue * @param string $response * @return void diff --git a/app/code/core/Mage/Usa/etc/wsdl/FedEx/TrackService_v5.wsdl b/app/code/core/Mage/Usa/etc/wsdl/FedEx/TrackService_v5.wsdl new file mode 100644 index 0000000000..1f275b3104 --- /dev/null +++ b/app/code/core/Mage/Usa/etc/wsdl/FedEx/TrackService_v5.wsdl @@ -0,0 +1,1510 @@ + + + + + + + + + + + + + + Descriptive data for a physical location. May be used as an actual physical address (place to which one could go), or as a container of "address parts" which should be handled as a unit (such as a city-state-ZIP combination within the US). + + + + + Combination of number, street name, etc. At least one line is required for a valid physical address; empty lines should not be included. + + + + + Name of city, town, etc. + + + + + Identifying abbreviation for US state, Canada province, etc. Format and presence of this field will vary, depending on country. + + + + + Identification of a region (usually small) for mail/package delivery. Format and presence of this field will vary, depending on country. + + + + + Relevant only to addresses in Puerto Rico. + + + + + The two-letter code used to identify a country. + + + + + Indicates whether this address residential (as opposed to commercial). + + + + + + + Identifies where a tracking event occurs. + + + + + + + + + + + + + + + + + + + + + + + + + + + Identification of a FedEx operating company (transportation). + + + + + + + + + + + + + Descriptive data for the client submitting a transaction. + + + + + The FedEx account number associated with this transaction. + + + + + This number is assigned by FedEx and identifies the unique device from which the request is originating + + + + + Only used in transactions which require identification of the Fed Ex Office integrator. + + + + + The language to be used for human-readable Notification.localizedMessages in responses to the request containing this ClientDetail object. Different requests from the same client may contain different Localization data. (Contrast with TransactionDetail.localization, which governs data payload language/translation.) + + + + + + + The descriptive data for a point-of-contact person. + + + + + Identifies the contact person's name. + + + + + Identifies the contact person's title. + + + + + Identifies the company this contact is associated with. + + + + + Identifies the phone number associated with this contact. + + + + + Identifies the phone extension associated with this contact. + + + + + Identifies the pager number associated with this contact. + + + + + Identifies the fax number associated with this contact. + + + + + Identifies the email address associated with this contact. + + + + + + + + + + + + + The dimensions of this package and the unit type used for the measurements. + + + + + + + + + + + Driving or other transportation distances, distinct from dimension measurements. + + + + + Identifies the distance quantity. + + + + + Identifies the unit of measure for the distance value. + + + + + + + Identifies the collection of units of measure that can be associated with a distance value. + + + + + + + + + Information describing email notifications that will be sent in relation to events that occur during package movement + + + + + A message that will be included in the email notifications + + + + + Information describing the destination of the email, format of the email and events to be notified on + + + + + + + + + + + + + + + The format of the email + + + + + + + + + + + + Identifies the relationship this email recipient has to the shipment. + + + + + The email address to send the notification to + + + + + The types of email notifications being requested for this recipient. + + + + + The format of the email notification. + + + + + The language/locale to be used in this email notification. + + + + + + + + + + + + + + + CM = centimeters, IN = inches + + + + + + + + + Identifies the representation of human-readable text. + + + + + Two-letter code for language (e.g. EN, FR, etc.) + + + + + Two-letter code for the region (e.g. us, ca, etc..). + + + + + + + The descriptive data regarding the result of the submitted transaction. + + + + + The severity of this notification. This can indicate success or failure or some other information about the request. The values that can be returned are SUCCESS - Your transaction succeeded with no other applicable information. NOTE - Additional information that may be of interest to you about your transaction. WARNING - Additional information that you need to know about your transaction that you may need to take action on. ERROR - Information about an error that occurred while processing your transaction. FAILURE - FedEx was unable to process your transaction at this time due to a system failure. Please try again later + + + + + Indicates the source of this notification. Combined with the Code it uniquely identifies this notification + + + + + A code that represents this notification. Combined with the Source it uniquely identifies this notification. + + + + + Human-readable text that explains this notification. + + + + + The translated message. The language and locale specified in the ClientDetail. Localization are used to determine the representation. Currently only supported in a TrackReply. + + + + + A collection of name/value pairs that provide specific data to help the client determine the nature of an error (or warning, etc.) witout having to parse the message string. + + + + + + + + + Identifies the type of data contained in Value (e.g. SERVICE_TYPE, PACKAGE_SEQUENCE, etc..). + + + + + The value of the parameter (e.g. PRIORITY_OVERNIGHT, 2, etc..). + + + + + + + Identifies the set of severity values for a Notification. + + + + + + + + + + + + + + + + + + + + Identification for a FedEx operating company (transportation and non-transportation). + + + + + + + + The enumerated packaging type used for this package. + + + + + + + + + + + + + + Tracking number and additional shipment data used to identify a unique shipment for proof of delivery. + + + + + FedEx assigned identifier for a package/shipment. + + + + + The date the package was shipped. + + + + + If the account number used to ship the package is provided in the request the shipper and recipient information is included on the letter or fax. + + + + + FedEx operating company that delivered the package. + + + + + Only country is used for elimination of duplicate tracking numbers. + + + + + + + + + + + + + + The service type of the package/shipment. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FedEx Signature Proof Of Delivery Fax reply. + + + + + This contains the severity type of the most severe Notification in the Notifications array. + + + + + Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply. + + + + + Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data. + + + + + Contains the version of the reply being used. + + + + + Confirmation of fax transmission. + + + + + + + FedEx Signature Proof Of Delivery Fax request. + + + + + Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services). + + + + + Descriptive data identifying the client submitting the transaction. + + + + + Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations. + + + + + The version of the request being used. + + + + + Tracking number and additional shipment data used to identify a unique shipment for proof of delivery. + + + + + Additional customer-supplied text to be added to the body of the letter. + + + + + Contact and address information about the person requesting the fax to be sent. + + + + + Contact and address information, including the fax number, about the person to receive the fax. + + + + + + + Identifies the set of SPOD image types. + + + + + + + + FedEx Signature Proof Of Delivery Letter reply. + + + + + This contains the severity type of the most severe Notification in the Notifications array. + + + + + Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply. + + + + + Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data. + + + + + Image of letter encoded in Base64 format. + + + + + Image of letter encoded in Base64 format. + + + + + + + FedEx Signature Proof Of Delivery Letter request. + + + + + Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services). + + + + + Descriptive data identifying the client submitting the transaction. + + + + + Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations. + + + + + The version of the request being used. + + + + + Tracking number and additional shipment data used to identify a unique shipment for proof of delivery. + + + + + Additional customer-supplied text to be added to the body of the letter. + + + + + Identifies the set of SPOD image types. + + + + + If provided this information will be print on the letter. + + + + + + + Each instance of this data type represents a barcode whose content must be represented as ASCII text (i.e. not binary data). + + + + + The kind of barcode data in this instance. + + + + + The data content of this instance. + + + + + + + + + + + + + + + + + The delivery location at the delivered to address. + + + + + + + + + + + + + + + + Detailed tracking information about a particular package. + + + + + To report soft error on an individual track detail. + + + + + The FedEx package identifier. + + + + + + When duplicate tracking numbers exist this data is returned with summary information for each of the duplicates. The summary information is used to determine which of the duplicates the intended tracking number is. This identifier is used on a subsequent track request to retrieve the tracking data for the desired tracking number. + + + + + A code that identifies this type of status. This is the most recent status. + + + + + A human-readable description of this status. + + + + + Used to report the status of a piece of a multiple piece shipment which is no longer traveling with the rest of the packages in the shipment or has not been accounted for. + + + + + Used to convey information such as. 1. FedEx has received information about a package but has not yet taken possession of it. 2. FedEx has handed the package off to a third party for final delivery. 3. The package delivery has been cancelled + + + + + Identifies a FedEx operating company (transportation). + + + + + Identifies operating transportation company that is the specific to the carrier code. + + + + + Specifies the FXO production centre contact and address. + + + + + Other related identifiers for this package such as reference numbers. + + + + + Retained for legacy compatibility only. User/screen friendly description of the Service type (e.g. Priority Overnight). + + + + + Strict representation of the Service type (e.g. PRIORITY_OVERNIGHT). + + + + + The weight of this package. + + + + + Physical dimensions of the package. + + + + + The dimensional weight of the package. + + + + + The weight of the entire shipment. + + + + + Retained for legacy compatibility only. + + + + + Strict representation of the Packaging type (e.g. FEDEX_BOX, YOUR_PACKAGING). + + + + + The sequence number of this package in a shipment. This would be 2 if it was package number 2 of 4. + + + + + The number of packages in this shipment. + + + + + + + The address information for the shipper. + + + + + The address of the FedEx pickup location/facility. + + + + + Estimated package pickup time for shipments that haven't been picked up. + + + + + Time package was shipped/tendered over to FedEx. Time portion will be populated if available, otherwise will be set to midnight. + + + + + The distance from the origin to the destination. Returned for Custom Critical shipments. + + + + + Total distance package still has to travel. Returned for Custom Critical shipments. + + + + + The address this package is to be (or has been) delivered. + + + + + The address of the FedEx delivery location/facility. + + + + + Projected package delivery time based on ship time stamp, service and destination. Not populated if delivery has already occurred. + + + + + The time the package was actually delivered. + + + + + Actual address where package was delivered. Differs from destinationAddress, which indicates where the package was to be delivered; This field tells where delivery actually occurred (next door, at station, etc.) + + + + + Identifies the method of office order delivery. + + + + + Strict text indicating the delivery location at the delivered to address. + + + + + User/screen friendly representation of the DeliveryLocationType (delivery location at the delivered to address). Can be returned in localized text. + + + + + This is either the name of the person that signed for the package or "Signature not requested" or "Signature on file". + + + + + True if signed for by signature image is available. + + + + + The types of email notifications that are available for the package. + + + + + Returned for cargo shipments only when they are currently split across vehicles. + + + + + Indicates redirection eligibility as determined by tracking service, subject to refinement/override by redirect-to-hold service. + + + + + Event information for a tracking number. + + + + + + + FedEx scanning information about a package. + + + + + The time this event occurred. + + + + + Carrier's scan code. Pairs with EventDescription. + + + + + Literal description that pairs with the EventType. + + + + + Further defines the Scan Type code's specific type (e.g., DEX08 business closed). Pairs with StatusExceptionDescription. + + + + + Literal description that pairs with the StatusExceptionCode. + + + + + Address information of the station that is responsible for the scan. + + + + + Indicates where the arrival actually occurred. + + + + + + + The type of track to be performed. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + FedEx assigned identifier for a package/shipment. + + + + + When duplicate tracking numbers exist this data is returned with summary information for each of the duplicates. The summary information is used to determine which of the duplicates the intended tracking number is. This identifier is used on a subsequent track request to retrieve the tracking data for the desired tracking number. + + + + + Identification of a FedEx operating company (transportation). + + + + + The date the package was shipped (tendered to FedEx). + + + + + The destination address of this package. Only city, state/province, and country are returned. + + + + + Options available for a tracking notification recipient. + + + + + + + Options available for a tracking notification recipient. + + + + + The types of email notifications available for this recipient. + + + + + + + FedEx Track Notification reply. + + + + + This contains the severity type of the most severe Notification in the Notifications array. + + + + + Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply. + + + + + Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data. + + + + + Contains the version of the reply being used. + + + + + True if duplicate packages (more than one package with the same tracking number) have been found, the packages array contains information about each duplicate. Use this information to determine which of the tracking numbers is the one you need and resend your request using the tracking number and TrackingNumberUniqueIdentifier for that package. + + + + + True if additional packages remain to be retrieved. + + + + + Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true). + + + + + Information about the notifications that are available for this tracking number. If there are duplicates the ship date and destination address information is returned for determining which TrackingNumberUniqueIdentifier to use on a subsequent request. + + + + + + + FedEx Track Notification request. + + + + + Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services). + + + + + Descriptive data identifying the client submitting the transaction. + + + + + Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations + + + + + Identifies the version/level of a service operation expected by a caller (in each request) and performed by the callee (in each reply). + + + + + The tracking number to which the notifications will be triggered from. + + + + + Indicates whether to return tracking information for all associated packages. + + + + + When the MoreDataAvailable field is true in a TrackNotificationReply the PagingToken must be sent in the subsequent TrackNotificationRequest to retrieve the next page of data. + + + + + Use this field when your original request informs you that there are duplicates of this tracking number. If you get duplicates you will also receive some information about each of the duplicate tracking numbers to enable you to chose one and resend that number along with the TrackingNumberUniqueId to get notifications for that tracking number. + + + + + To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. + + + + + To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. + + + + + Included in the email notification identifying the requester of this notification. + + + + + Included in the email notification identifying the requester of this notification. + + + + + Who to send the email notifications to and for which events. The notificationRecipientType and NotifyOnShipment fields are not used in this request. + + + + + + + The type and value of the package identifier that is to be used to retrieve the tracking information for a package. + + + + + The value to be used to retrieve tracking information for a package. + + + + + The type of the Value to be used to retrieve tracking information for a package (e.g. SHIPPER_REFERENCE, PURCHASE_ORDER, TRACKING_NUMBER_OR_DOORTAG, etc..) . + + + + + + + Used to report the status of a piece of a multiple piece shipment which is no longer traveling with the rest of the packages in the shipment or has not been accounted for. + + + + + An identifier for this type of status. + + + + + A human-readable description of this status. + + + + + + + The descriptive data returned from a FedEx package tracking request. + + + + + This contains the severity type of the most severe Notification in the Notifications array. + + + + + Information about the request/reply such was the transaction successful or not, and any additional information relevant to the request and/or reply. There may be multiple Notifications in a reply. + + + + + Contains the CustomerTransactionDetail that is echoed back to the caller for matching requests and replies and a Localization element for defining the language/translation used in the reply data. + + + + + Contains the version of the reply being used. + + + + + True if duplicate packages (more than one package with the same tracking number) have been found, and only limited data will be provided for each one. + + + + + True if additional packages remain to be retrieved. + + + + + Value that must be passed in a TrackNotification request to retrieve the next set of packages (when MoreDataAvailable = true). + + + + + Contains detailed tracking information for the requested packages(s). + + + + + + + The descriptive data sent by a client to track a FedEx package. + + + + + Descriptive data to be used in authentication of the sender's identity (and right to use FedEx web services). + + + + + Descriptive data identifying the client submitting the transaction. + + + + + Contains a free form field that is echoed back in the reply to match requests with replies and data that governs the data payload language/translations. + + + + + The version of the request being used. + + + + + The FedEx operating company (transportation) used for this package's delivery. + + + + + Identifies operating transportation company that is the specific to the carrier code. + + + + + The type and value of the package identifier that is to be used to retrieve the tracking information for a package or group of packages. + + + + + Used to distinguish duplicate FedEx tracking numbers. + + + + + To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. + + + + + To narrow the search to a period in time the ShipDateRangeBegin and ShipDateRangeEnd can be used to help eliminate duplicates. + + + + + For tracking by references information either the account number or destination postal code and country must be provided. + + + + + For tracking by references information either the account number or destination postal code and country must be provided. + + + + + If false the reply will contain summary/profile data including current status. If true the reply contains profile + detailed scan activity for each package. + + + + + When the MoreData field = true in a TrackReply the PagingToken must be sent in the subsequent TrackRequest to retrieve the next page of data. + + + + + + + + + + + + + Used when a cargo shipment is split across vehicles. This is used to give the status of each part of the shipment. + + + + + The number of pieces in this part. + + + + + The date and time this status began. + + + + + A code that identifies this type of status. + + + + + A human-readable description of this status. + + + + + + + Descriptive data that governs data payload language/translations. The TransactionDetail from the request is echoed back to the caller in the corresponding reply. + + + + + Free form text to be echoed back in the reply. Used to match requests and replies. + + + + + Governs data payload language/translations (contrasted with ClientDetail.localization, which governs Notification.localizedMessage language selection). + + + + + + + The descriptive data for the heaviness of an object. + + + + + Identifies the unit of measure associated with a weight value. + + + + + Identifies the weight value of a package/shipment. + + + + + + + Identifies the collection of units of measure that can be associated with a weight value. + + + + + + + + + Used in authentication of the sender's identity. + + + + + Credential used to authenticate a specific software application. This value is provided by FedEx after registration. + + + + + + + Two part authentication string used for the sender's identity + + + + + Identifying part of authentication credential. This value is provided by FedEx after registration + + + + + Secret part of authentication key. This value is provided by FedEx after registration. + + + + + + + Identifies the version/level of a service operation expected by a caller (in each request) and performed by the callee (in each reply). + + + + + Identifies a system or sub-system which performs an operation. + + + + + Identifies the service business level. + + + + + Identifies the service interface level. + + + + + Identifies the service code level. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/code/core/Mage/Wishlist/Model/Wishlist.php b/app/code/core/Mage/Wishlist/Model/Wishlist.php index 07cc2f361c..df9965b8ed 100644 --- a/app/code/core/Mage/Wishlist/Model/Wishlist.php +++ b/app/code/core/Mage/Wishlist/Model/Wishlist.php @@ -519,7 +519,9 @@ public function updateItem($itemId, $buyRequest, $params = null) $isForceSetQuantity = true; foreach ($items as $_item) { /* @var $_item Mage_Wishlist_Model_Item */ - if (($_item->getProductId() == $product->getId()) && $_item->representProduct($product)) { + if ($_item->getProductId() == $product->getId() + && $_item->representProduct($product) + && $_item->getId() != $item->getId()) { // We do not add new wishlist item, but updating the existing one $isForceSetQuantity = false; } diff --git a/app/code/core/Mage/XmlConnect/Helper/Data.php b/app/code/core/Mage/XmlConnect/Helper/Data.php index 4b74798e4b..daf287f808 100644 --- a/app/code/core/Mage/XmlConnect/Helper/Data.php +++ b/app/code/core/Mage/XmlConnect/Helper/Data.php @@ -568,7 +568,7 @@ public function getApplicationOptions() $options = array(); /** @var $app Mage_XmlConnect_Model_Application */ foreach (Mage::getModel('xmlconnect/application')->getCollection() as $app) { - $options[] = array('value' => $app->getCode(), 'label' => $app->getName()); + $options[] = array('value' => $app->getId(), 'label' => $app->getName()); } if (count($options) > 1) { array_unshift($options, array( diff --git a/app/code/core/Mage/XmlConnect/Model/Application.php b/app/code/core/Mage/XmlConnect/Model/Application.php index d8ba56aaac..fe57afaccd 100644 --- a/app/code/core/Mage/XmlConnect/Model/Application.php +++ b/app/code/core/Mage/XmlConnect/Model/Application.php @@ -841,9 +841,7 @@ public function loadSubmit() $conf['submit_restore'] = array(); } foreach ($params as $id => $value) { - $deviceImages = Mage::helper('xmlconnect') - ->getDeviceHelper() - ->getSubmitImages(); + $deviceImages = Mage::helper('xmlconnect')->getDeviceHelper()->getSubmitImages(); if (!in_array($id, $deviceImages)) { $conf['submit_text'][$id] = $value; @@ -1055,7 +1053,8 @@ public function prepareSubmitParams($data) foreach ($deviceImages as $id) { if (isset($submit[$id])) { - $params[$id] = '@' . $submit[$id]; + $params[$id] = '@' . Mage::helper('xmlconnect/image')->getDefaultSizeUploadDir() . DS + . $submit[$id]; } elseif (isset($submitRestore[$id])) { $params[$id] = $submitRestore[$id]; } diff --git a/app/design/adminhtml/default/default/template/page/head.phtml b/app/design/adminhtml/default/default/template/page/head.phtml index c190c099f5..8f8b5ba647 100644 --- a/app/design/adminhtml/default/default/template/page/head.phtml +++ b/app/design/adminhtml/default/default/template/page/head.phtml @@ -1,6 +1,8 @@ +getCanLoadTinyMce()): ?> + + <?php echo htmlspecialchars(html_entity_decode($this->getTitle())) ?> - diff --git a/app/design/adminhtml/default/default/template/sales/order/totals/tax.phtml b/app/design/adminhtml/default/default/template/sales/order/totals/tax.phtml index 0000da9eb0..dd43abc3d1 100644 --- a/app/design/adminhtml/default/default/template/sales/order/totals/tax.phtml +++ b/app/design/adminhtml/default/default/template/sales/order/totals/tax.phtml @@ -64,7 +64,6 @@ $_fullInfo = $this->getFullTaxInfo(); - + + + diff --git a/app/design/frontend/base/default/layout/paypaluk.xml b/app/design/frontend/base/default/layout/paypaluk.xml index bdc029a45c..1566812990 100644 --- a/app/design/frontend/base/default/layout/paypaluk.xml +++ b/app/design/frontend/base/default/layout/paypaluk.xml @@ -130,6 +130,10 @@ + + + + diff --git a/app/design/frontend/base/default/template/catalog/product/price_msrp_item.phtml b/app/design/frontend/base/default/template/catalog/product/price_msrp_item.phtml index fb464b2765..7937f0abad 100644 --- a/app/design/frontend/base/default/template/catalog/product/price_msrp_item.phtml +++ b/app/design/frontend/base/default/template/catalog/product/price_msrp_item.phtml @@ -42,6 +42,7 @@ $_product = $this->getProduct(); $_id = $_product->getId(); $_msrpPrice = ''; + $priceElementIdPrefix = $this->getPriceElementIdPrefix() ? $this->getPriceElementIdPrefix() : 'product-price-'; ?>
helper('tax')->getPrice($_product, $_product->getMsrp()) ?> @@ -51,7 +52,7 @@ isShowPriceOnGesture($_product)): ?> - getIdSuffix(); ?> + getIdSuffix(); ?> getRandomString(20); ?> __('Click for price'); ?> diff --git a/app/design/frontend/base/default/template/paypal/express/review.phtml b/app/design/frontend/base/default/template/paypal/express/review.phtml index 2a0ca05789..d93e650d24 100644 --- a/app/design/frontend/base/default/template/paypal/express/review.phtml +++ b/app/design/frontend/base/default/template/paypal/express/review.phtml @@ -120,8 +120,8 @@
getChildHtml('agreements'); ?>
- - + + diff --git a/app/design/install/default/default/template/install/db/main.phtml b/app/design/install/default/default/template/install/db/main.phtml index 5358cf2403..c40468fcd3 100644 --- a/app/design/install/default/default/template/install/db/main.phtml +++ b/app/design/install/default/default/template/install/db/main.phtml @@ -63,13 +63,18 @@ function showContent(select) - - getDatabaseBlock($block->getIdPrefix()) ?> -
getFormData()->getDbModel() != $block->getIdPrefix()):?>style="display:none;">toHtml(); ?>
+ + getDatabaseBlock($block->getIdPrefix()) ?> + +
getFormData()->getDbModel() != $block->getIdPrefix()):?>style="display:none;"> +
    + toHtml(); ?> +
+
diff --git a/app/locale/en_US/Mage_Adminhtml.csv b/app/locale/en_US/Mage_Adminhtml.csv index cb09b3eb06..3c533d9f6e 100644 --- a/app/locale/en_US/Mage_Adminhtml.csv +++ b/app/locale/en_US/Mage_Adminhtml.csv @@ -139,7 +139,6 @@ "Attribute Set Name:","Attribute Set Name:" "Attributes","Attributes" "Automatic","Automatic" -"Available Products","Available Products" "Average Order Amount","Average Order Amount" "Average Orders","Average Orders" "BINARY","BINARY" @@ -234,7 +233,6 @@ "Currency Information","Currency Information" "Currency Setup Section","Currency Setup Section" "Current Configuration Scope:","Current Configuration Scope:" -"Current Mapping will be reloaded. Continue?","Current Mapping will be reloaded. Continue?" "Current Month","Current Month" "Custom","Custom" "Custom Variable ""%s""","Custom Variable ""%s""" @@ -382,7 +380,7 @@ "Go to messages inbox","Go to messages inbox" "Go to notifications","Go to notifications" "Google Base","Google Base" -"Google Content Items","Google Content Items" +"Google Content","Google Content" "Google Sitemaps","Google Sitemaps" "Grand Total","Grand Total" "Grid (default) / List","Grid (default) / List" @@ -563,6 +561,7 @@ "New User","New User" "New Variable","New Variable" "New Website","New Website" +"New attribute set mapping","New attribute set mapping" "New password field cannot be empty.","New password field cannot be empty." "Newsletter","Newsletter" "Newsletter Problems","Newsletter Problems" @@ -689,7 +688,6 @@ "Please wait...","Please wait..." "Please, add some answers to this poll first.","Please, add some answers to this poll first." "Please, select ""Visible in Stores"" for this poll first.","Please, select ""Visible in Stores"" for this poll first." -"Please, select Attribute Set and Google Item Type to load attributes","Please, select Attribute Set and Google Item Type to load attributes" "Poll Manager","Poll Manager" "Polls","Polls" "Popular","Popular" diff --git a/app/locale/en_US/Mage_Catalog.csv b/app/locale/en_US/Mage_Catalog.csv index 3261c9b32d..7c10041ff9 100644 --- a/app/locale/en_US/Mage_Catalog.csv +++ b/app/locale/en_US/Mage_Catalog.csv @@ -16,6 +16,7 @@ "AM","AM" "Action","Action" "Actual Price","Actual Price" +"Add","Add" "Add Attribute","Add Attribute" "Add Design Change","Add Design Change" "Add Group","Add Group" @@ -34,6 +35,11 @@ "Add Subcategory","Add Subcategory" "Add Tax","Add Tax" "Add Tier","Add Tier" +"Add attribute into attribute set","Add attribute into attribute set" +"Add group into attribute set","Add group into attribute set" +"Add new custom option into product","Add new custom option into product" +"Add new values into custom option","Add new values into custom option" +"Add option","Add option" "Add to Cart","Add to Cart" "Add to Compare","Add to Compare" "Add to Wishlist","Add to Wishlist" @@ -87,7 +93,10 @@ "Attribute Model","Attribute Model" "Attribute Name:","Attribute Name:" "Attribute Set","Attribute Set" +"Attribute Sets","Attribute Sets" +"Attribute add","Attribute add" "Attribute group with the \"/name/\" name already exists","Attribute group with the \"/name/\" name already exists" +"Attribute remove","Attribute remove" "Attribute with the same code already exists","Attribute with the same code already exists" "Attributes","Attributes" "Attrribute names can be specified per store.","Attrribute names can be specified per store." @@ -144,6 +153,8 @@ "Catalog Seo Sitemap (Common)","Catalog Seo Sitemap (Common)" "Catalog Seo Sitemap (Product List)","Catalog Seo Sitemap (Product List)" "Catalog URL Rewrites","Catalog URL Rewrites" +"Catalog product custom option values API","Catalog product custom option values API" +"Catalog product custom options API","Catalog product custom options API" "Catalog, Search","Catalog, Search" "Categories","Categories" "Categories Sitemap","Categories Sitemap" @@ -192,12 +203,15 @@ "Create Permanent Redirect for old URL","Create Permanent Redirect for old URL" "Create Product Settings","Create Product Settings" "Create Simple Associated Product","Create Simple Associated Product" +"Create attribute set based on another set","Create attribute set based on another set" +"Create new attribute","Create new attribute" "Create new category","Create new category" "Create new product","Create new product" "Cross-sells","Cross-sells" "Currently Shopping by:","Currently Shopping by:" "Custom Design","Custom Design" "Custom Options","Custom Options" +"Custom options","Custom options" "Customer Group","Customer Group" "Customers Reviews","Customers Reviews" "Customers Tagged Product","Customers Tagged Product" @@ -223,6 +237,7 @@ "Delete Row","Delete Row" "Delete Search","Delete Search" "Delete Selected Group","Delete Selected Group" +"Delete attribute","Delete attribute" "Delete category","Delete category" "Delete product","Delete product" "Depends on design theme","Depends on design theme" @@ -275,11 +290,18 @@ "Frontend Properties","Frontend Properties" "Gallery","Gallery" "General Information","General Information" +"Get full information about attribute with list of options","Get full information about attribute with list of options" +"Get full information about custom option in product","Get full information about custom option in product" +"Get list of available custom option types","Get list of available custom option types" +"Get list of non-default attributes by product type and attributes set","Get list of non-default attributes by product type and attributes set" +"Get list of possible attribute types","Get list of possible attribute types" "Get special price","Get special price" "Global","Global" "Globally Editable","Globally Editable" "Go to Home Page","Go to Home Page" "Grid","Grid" +"Group add","Group add" +"Group remove","Group remove" "Grouped Product","Grouped Product" "Groups","Groups" "Home","Home" @@ -301,6 +323,7 @@ "Index product attributes for layered navigation building","Index product attributes for layered navigation building" "Index product prices","Index product prices" "Indexed category/products association","Indexed category/products association" +"Info","Info" "Info Column Options Wrapper","Info Column Options Wrapper" "Input Type","Input Type" "Integer","Integer" @@ -339,6 +362,7 @@ "List","List" "List Mode","List Mode" "List of Products that are set as New","List of Products that are set as New" +"List of types","List of types" "Manage Attribute Sets","Manage Attribute Sets" "Manage Attributes","Manage Attributes" "Manage Catalog Categories","Manage Catalog Categories" @@ -397,7 +421,9 @@ "Old Price:","Old Price:" "On Gesture","On Gesture" "Option validation failed to add product to cart.","Option validation failed to add product to cart." +"Option values","Option values" "Option:","Option:" +"Options","Options" "Options Control","Options Control" "Options is required","Options is required" "Our customer service is available 24/7. Call us at (555) 555-0123.","Our customer service is available 24/7. Call us at (555) 555-0123." @@ -503,9 +529,17 @@ "Remove","Remove" "Remove Product From Websites","Remove Product From Websites" "Remove This Item","Remove This Item" +"Remove attribute from attribute set","Remove attribute from attribute set" +"Remove attribute set","Remove attribute set" +"Remove custom option","Remove custom option" +"Remove group from attribute set","Remove group from attribute set" +"Remove option","Remove option" "Remove product assignment","Remove product assignment" "Remove product image","Remove product image" "Remove product link","Remove product link" +"Remove value from custom option","Remove value from custom option" +"Rename existing group","Rename existing group" +"Rename group","Rename group" "Reorganize EAV category structure to flat structure","Reorganize EAV category structure to flat structure" "Reorganize EAV product structure to flat structure","Reorganize EAV product structure to flat structure" "Reset","Reset" @@ -519,7 +553,10 @@ "Retrieve hierarchical tree","Retrieve hierarchical tree" "Retrieve linked products","Retrieve linked products" "Retrieve list of assigned products","Retrieve list of assigned products" +"Retrieve list of option values","Retrieve list of option values" +"Retrieve list of product custom options","Retrieve list of product custom options" "Retrieve one level of categories by website/store view/parent category","Retrieve one level of categories by website/store view/parent category" +"Retrieve option value info","Retrieve option value info" "Retrieve product","Retrieve product" "Retrieve product attribute sets","Retrieve product attribute sets" "Retrieve product image","Retrieve product image" @@ -657,6 +694,7 @@ "Top/Left","Top/Left" "Top/Right","Top/Right" "Total Qty Base Items","Total Qty Base Items" +"Total Qty Content Items","Total Qty Content Items" "Total incl. Tax: %1$s","Total incl. Tax: %1$s" "Total of %d record(s) have been deleted.","Total of %d record(s) have been deleted." "Total of %d record(s) have been updated.","Total of %d record(s) have been updated." @@ -675,12 +713,15 @@ "Update Attributes","Update Attributes" "Update Tier Price","Update Tier Price" "Update assigned product","Update assigned product" +"Update attribute","Update attribute" "Update attributes","Update attributes" "Update category","Update category" +"Update custom option of product","Update custom option of product" "Update product","Update product" "Update product image","Update product image" "Update product link","Update product link" "Update product tier prices","Update product tier prices" +"Update value of custom option","Update value of custom option" "Upload new product image ","Upload new product image " "Url Rewrite Management","Url Rewrite Management" "Use Canonical Link Meta Tag For Categories","Use Canonical Link Meta Tag For Categories" diff --git a/app/locale/en_US/Mage_Core.csv b/app/locale/en_US/Mage_Core.csv index c0f9ac2621..9e8b14263d 100644 --- a/app/locale/en_US/Mage_Core.csv +++ b/app/locale/en_US/Mage_Core.csv @@ -56,6 +56,7 @@ "Cookie Lifetime","Cookie Lifetime" "Cookie Path","Cookie Path" "Copyright","Copyright" +"Core","Core" "Countries Options","Countries Options" "Create Store","Create Store" "Create Store View","Create Store View" @@ -148,6 +149,7 @@ "JavaScript Settings","JavaScript Settings" "Layout","Layout" "Leave empty for access from any location.","Leave empty for access from any location." +"List of stores","List of stores" "Locale","Locale" "Locale Options","Locale Options" "Log Settings","Log Settings" @@ -246,6 +248,8 @@ "Requested file may not include parent directory traversal (""../"", ""..\\ notation)""","Requested file may not include parent directory traversal (""../"", ""..\\ notation)""" "Requested invalid store ""%s""","Requested invalid store ""%s""" "Resource is not set.","Resource is not set." +"Retrieve store data","Retrieve store data" +"Retrieve store list","Retrieve store list" "Return-Path Email","Return-Path Email" "Root Category","Root Category" "Sales Representative","Sales Representative" @@ -274,6 +278,7 @@ "Status","Status" "Storage Configuration for Media","Storage Configuration for Media" "Store","Store" +"Store API","Store API" "Store Contact Address","Store Contact Address" "Store Contact Information","Store Contact Information" "Store Contact Telephone","Store Contact Telephone" diff --git a/app/locale/en_US/Mage_Downloadable.csv b/app/locale/en_US/Mage_Downloadable.csv index 9b85d26adc..762ebb2527 100644 --- a/app/locale/en_US/Mage_Downloadable.csv +++ b/app/locale/en_US/Mage_Downloadable.csv @@ -1,4 +1,6 @@ +"Add","Add" "Add New Row","Add New Row" +"Add links and samples to downloadable product","Add links and samples to downloadable product" "Alphanumeric, dash and underscore characters are recommended for filenames. Improper characters are replaced with \'_\'.","Alphanumeric, dash and underscore characters are recommended for filenames. Improper characters are replaced with \'_\'." "An error occurred while getting requested content. Please contact the store owner.","An error occurred while getting requested content. Please contact the store owner." "An error occurred while getting the requested content.","An error occurred while getting the requested content." @@ -9,6 +11,7 @@ "Canceled","Canceled" "Cannot connect to remote host, error: %s.","Cannot connect to remote host, error: %s." "Catalog Product View (Downloadable)","Catalog Product View (Downloadable)" +"Category API","Category API" "Customer My Account Downloadable Items","Customer My Account Downloadable Items" "Date","Date" "Default Link Title","Default Link Title" @@ -35,6 +38,7 @@ "Invalid download link type.","Invalid download link type." "Invoiced","Invoiced" "Links can be purchased separately","Links can be purchased separately" +"List","List" "Max. Downloads","Max. Downloads" "Message:","Message:" "Move to Wishlist","Move to Wishlist" @@ -51,11 +55,15 @@ "Please set resource file and link type.","Please set resource file and link type." "Please specify product link(s).","Please specify product link(s)." "Price","Price" +"Product downloadable links","Product downloadable links" "Qty","Qty" "Refunded","Refunded" "Remaining Downloads","Remaining Downloads" +"Remove","Remove" "Remove Item","Remove Item" +"Remove links and samples from downloadable product","Remove links and samples from downloadable product" "Requested link does not exist.","Requested link does not exist." +"Retrieve links and samples list from downloadable product","Retrieve links and samples list from downloadable product" "Sample","Sample" "See price before order confirmation.","See price before order confirmation." "Shareable","Shareable" diff --git a/app/locale/en_US/Mage_GoogleCheckout.csv b/app/locale/en_US/Mage_GoogleCheckout.csv index 0d3a9e9a72..59144438c1 100644 --- a/app/locale/en_US/Mage_GoogleCheckout.csv +++ b/app/locale/en_US/Mage_GoogleCheckout.csv @@ -70,6 +70,7 @@ "New Order Status","New Order Status" "No","No" "Optional, leave empty for home page.","Optional, leave empty for home page." +"Order creation error","Order creation error" "Rate 1 Amount","Rate 1 Amount" "Rate 1 Ship To Applicable Countries","Rate 1 Ship To Applicable Countries" "Rate 1 Ship to Specific Countries","Rate 1 Ship to Specific Countries" diff --git a/app/locale/en_US/Mage_Sales.csv b/app/locale/en_US/Mage_Sales.csv index 1872019bfc..ac04eb30b6 100644 --- a/app/locale/en_US/Mage_Sales.csv +++ b/app/locale/en_US/Mage_Sales.csv @@ -36,6 +36,7 @@ "Add To Order","Add To Order" "Add Tracking Number","Add Tracking Number" "Add comment to order","Add comment to order" +"Add new comment to credit memo","Add new comment to credit memo" "Add new comment to shipment","Add new comment to shipment" "Add new tracking number","Add new tracking number" "Add to Cart","Add to Cart" @@ -96,6 +97,7 @@ "Buy %s for price %s","Buy %s for price %s" "CSV","CSV" "Cancel","Cancel" +"Cancel credit memo","Cancel credit memo" "Cancel invoice","Cancel invoice" "Cancel order","Cancel order" "Canceled","Canceled" @@ -153,6 +155,7 @@ "Create Shipment","Create Shipment" "Create Shipping Label","Create Shipping Label" "Create Shipping Label...","Create Shipping Label..." +"Create new credit memo for order","Create new credit memo for order" "Create new invoice for order","Create new invoice for order" "Create new shipment for order","Create new shipment for order" "Create...","Create..." @@ -173,6 +176,8 @@ "Credit Memo Update","Credit Memo Update" "Credit Memo Update for Guest","Credit Memo Update for Guest" "Credit Memos","Credit Memos" +"Credit memo canceling problem.","Credit memo canceling problem." +"Credit memo cannot be canceled.","Credit memo cannot be canceled." "Credit memo has been created automatically","Credit memo has been created automatically" "Creditmemo","Creditmemo" "Custom Price","Custom Price" @@ -436,6 +441,7 @@ "Order Update","Order Update" "Order Update for Guest","Order Update for Guest" "Order View","Order View" +"Order credit memo","Order credit memo" "Order does not allow to be canceled.","Order does not allow to be canceled." "Order invoice","Order invoice" "Order is suspended as its capture amount %s is suspected to be fraudulent.","Order is suspended as its capture amount %s is suspected to be fraudulent." @@ -574,8 +580,12 @@ "Remove Coupon Code","Remove Coupon Code" "Remove tracking number","Remove tracking number" "Reorder","Reorder" +"Retrieve credit memo info","Retrieve credit memo info" +"Retrieve credit memo information","Retrieve credit memo information" +"Retrieve credit memo list","Retrieve credit memo list" "Retrieve invoice info","Retrieve invoice info" "Retrieve invoice information","Retrieve invoice information" +"Retrieve list of credit memos by filters","Retrieve list of credit memos by filters" "Retrieve list of invoices by filters","Retrieve list of invoices by filters" "Retrieve list of orders by filters","Retrieve list of orders by filters" "Retrieve list of shipments by filters","Retrieve list of shipments by filters" diff --git a/app/locale/en_US/Mage_Tag.csv b/app/locale/en_US/Mage_Tag.csv index 3d2bb89ee9..752777ffcb 100644 --- a/app/locale/en_US/Mage_Tag.csv +++ b/app/locale/en_US/Mage_Tag.csv @@ -1,9 +1,11 @@ "# of Uses","# of Uses" "Tag Name: %s","Tag Name: %s" "Action","Action" +"Add","Add" "Add New Tag","Add New Tag" "Add Tags","Add Tags" "Add Your Tags:","Add Your Tags:" +"Add tag(s) to product","Add tag(s) to product" "Add to Cart","Add to Cart" "Add to Wishlist","Add to Wishlist" "All Tags","All Tags" @@ -35,6 +37,7 @@ "General Information","General Information" "Grid","Grid" "ID","ID" +"Info","Info" "Last Name","Last Name" "List","List" "Manage Tags","Manage Tags" @@ -51,6 +54,7 @@ "Price","Price" "Product Name","Product Name" "Product SKU","Product SKU" +"Product Tag API","Product Tag API" "Product Tags","Product Tags" "Products","Products" "Products Tagged by Administrators","Products Tagged by Administrators" @@ -58,6 +62,10 @@ "Products Tagged with '%s'","Products Tagged with '%s'" "Products tagged with '%s'","Products tagged with '%s'" "Rebuild Tag aggregation data","Rebuild Tag aggregation data" +"Remove","Remove" +"Remove product tag","Remove product tag" +"Retrieve list of tags by product","Retrieve list of tags by product" +"Retrieve product tag info","Retrieve product tag info" "SKU","SKU" "Save Tag","Save Tag" "Save and Continue Edit","Save and Continue Edit" @@ -80,6 +88,8 @@ "Unable to find any products tagged with '%s' in the current store","Unable to find any products tagged with '%s' in the current store" "Unable to remove tag. Please, try again later.","Unable to remove tag. Please, try again later." "Unable to save tag(s).","Unable to save tag(s)." +"Update","Update" +"Update product tag","Update product tag" "Use spaces to separate tags. Use single quotes (') for phrases.","Use spaces to separate tags. Use single quotes (') for phrases." "View All Tags","View All Tags" "View Customers","View Customers" diff --git a/js/varien/product.js b/js/varien/product.js index 61428cd58b..ca87f10f2f 100644 --- a/js/varien/product.js +++ b/js/varien/product.js @@ -280,12 +280,12 @@ Product.Config.prototype = { $(this.settings[i]).nextSetting = nextSetting; childSettings.push(this.settings[i]); } - + // Set default values - from config and overwrite them by url values if (config.defaultValues) { this.values = config.defaultValues; } - + var separatorIndex = window.location.href.indexOf('#'); if (separatorIndex != -1) { var paramsStr = window.location.href.substr(separatorIndex+1); @@ -301,7 +301,7 @@ Product.Config.prototype = { this.configureForValues(); document.observe("dom:loaded", this.configureForValues.bind(this)); }, - + configureForValues: function () { if (this.values) { this.settings.each(function(element){ @@ -599,7 +599,7 @@ Product.OptionsPrice.prototype = { var nonTaxable = 0; var oldPrice = 0; var priceInclTax = 0; - var currentTax = this.currentTax; + var currentTax = this.currentTax; $H(this.optionPrices).each(function(pair) { if ('undefined' != typeof(pair.value.price) && 'undefined' != typeof(pair.value.oldPrice)) { price += parseFloat(pair.value.price); @@ -609,7 +609,7 @@ Product.OptionsPrice.prototype = { } else if (pair.key == 'priceInclTax') { priceInclTax += pair.value; } else if (pair.key == 'optionsPriceInclTax') { - priceInclTax += pair.value * (100 + currentTax) / 100; + priceInclTax += pair.value * (100 + currentTax) / 100; } else { price += parseFloat(pair.value); oldPrice += parseFloat(pair.value); @@ -632,6 +632,7 @@ Product.OptionsPrice.prototype = { var _productPrice; var _plusDisposition; var _minusDisposition; + var _priceInclTax; if ($(pair.value)) { if (pair.value == 'old-price-'+this.productId && this.productOldPrice != this.productPrice) { _productPrice = this.productOldPrice; @@ -642,20 +643,21 @@ Product.OptionsPrice.prototype = { _plusDisposition = this.plusDisposition; _minusDisposition = this.minusDisposition; } + _priceInclTax = priceInclTax; if (pair.value == 'old-price-'+this.productId && optionOldPrice !== undefined) { price = optionOldPrice+parseFloat(_productPrice); } else if (this.specialTaxPrice == 'true' && this.priceInclTax !== undefined && this.priceExclTax !== undefined) { price = optionPrices+parseFloat(this.priceExclTax); - priceInclTax += this.priceInclTax; + _priceInclTax += this.priceInclTax; } else { price = optionPrices+parseFloat(_productPrice); - priceInclTax += parseFloat(_productPrice) * (100 + this.currentTax) / 100; + _priceInclTax += parseFloat(_productPrice) * (100 + this.currentTax) / 100; } if (this.specialTaxPrice == 'true') { var excl = price; - var incl = priceInclTax; + var incl = _priceInclTax; } else if (this.includeTax == 'true') { // tax = tax included into product price by admin var tax = price / (100 + this.defaultTax) * this.defaultTax; diff --git a/skin/frontend/base/default/js/msrp.js b/skin/frontend/base/default/js/msrp.js index bafe0b6675..382971f256 100644 --- a/skin/frontend/base/default/js/msrp.js +++ b/skin/frontend/base/default/js/msrp.js @@ -262,6 +262,8 @@ Catalog.Map = { productAddToCartForm.submitLight = productAddToCartFormOld.submitLight; }else if(!$('product_addtocart_form_from_popup')) { return false; + } else if ('undefined' == typeof productAddToCartForm) { + productAddToCartForm = new VarienForm('product_addtocart_form_from_popup'); } productAddToCartForm.submit = function(button, url) { if (('undefined' != typeof productAddToCartFormOld) && productAddToCartFormOld) { diff --git a/var/package/Interface_Adminhtml_Default-1.6.1.0-rc1.xml b/var/package/Interface_Adminhtml_Default-1.6.1.0.xml similarity index 99% rename from var/package/Interface_Adminhtml_Default-1.6.1.0-rc1.xml rename to var/package/Interface_Adminhtml_Default-1.6.1.0.xml index da501192c3..32f60cf2ac 100644 --- a/var/package/Interface_Adminhtml_Default-1.6.1.0-rc1.xml +++ b/var/package/Interface_Adminhtml_Default-1.6.1.0.xml @@ -1,18 +1,18 @@ Interface_Adminhtml_Default - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable AFL v3.0 community Default interface for Adminhtml Default interface for Adminhtml - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - + 2011-10-19 + + - 5.2.06.0.0Mage_Core_Adminhtmlcommunity1.6.0.01.6.1.0Lib_Js_Extcommunity1.6.0.01.6.1.0 + 5.2.06.0.0Mage_Core_Adminhtmlcommunity1.6.1.01.7.0.0Lib_Js_Extcommunity1.6.0.01.7.0.0 diff --git a/var/package/Interface_Frontend_Base_Default-1.6.1.0-rc1.xml b/var/package/Interface_Frontend_Base_Default-1.6.1.0.xml similarity index 97% rename from var/package/Interface_Frontend_Base_Default-1.6.1.0-rc1.xml rename to var/package/Interface_Frontend_Base_Default-1.6.1.0.xml index 6b136e83fb..a75c2dd057 100644 --- a/var/package/Interface_Frontend_Base_Default-1.6.1.0-rc1.xml +++ b/var/package/Interface_Frontend_Base_Default-1.6.1.0.xml @@ -1,18 +1,18 @@ Interface_Frontend_Base_Default - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable AFL v3.0 community This is a Magento themes base This is a Magento themes base - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - + 2011-10-19 + + - 5.2.06.0.0Mage_Core_Modulescommunity1.6.0.01.6.1.0 + 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.01.7.0.0 diff --git a/var/package/Interface_Install_Default-1.6.0.0.xml b/var/package/Interface_Install_Default-1.6.1.0.xml similarity index 93% rename from var/package/Interface_Install_Default-1.6.0.0.xml rename to var/package/Interface_Install_Default-1.6.1.0.xml index 069be3574c..0a22c2be98 100644 --- a/var/package/Interface_Install_Default-1.6.0.0.xml +++ b/var/package/Interface_Install_Default-1.6.1.0.xml @@ -1,18 +1,18 @@ Interface_Install_Default - 1.6.0.0 + 1.6.1.0 stable AFL v3.0 community Default interface for Install Default interface for Install - 1.6.0.0 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-08-18 - - + 2011-10-19 + + - 5.2.06.0.0Mage_Core_Modulescommunity1.6.0.01.6.1.0 + 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.01.7.0.0 diff --git a/var/package/Lib_Google_Checkout-1.5.0.0.xml b/var/package/Lib_Google_Checkout-1.5.0.0.xml index 591ef813d3..2266199a18 100644 --- a/var/package/Lib_Google_Checkout-1.5.0.0.xml +++ b/var/package/Lib_Google_Checkout-1.5.0.0.xml @@ -10,8 +10,8 @@ Google Checkout Library 1.5.0.0 Magento Core Teamcorecore@magentocommerce.com - 2011-07-11 - + 2011-07-07 + 5.2.06.0.0 diff --git a/var/package/Lib_Js_Calendar-1.51.1.xml b/var/package/Lib_Js_Calendar-1.51.1.xml index 5b74230e15..2d7c9ef3e7 100644 --- a/var/package/Lib_Js_Calendar-1.51.1.xml +++ b/var/package/Lib_Js_Calendar-1.51.1.xml @@ -10,8 +10,8 @@ Javascript Calendar for Magento 1.51.1 Magento Core Teamcorecore@magentocommerce.com - 2011-07-11 - + 2011-07-07 + 5.2.06.0.0 diff --git a/var/package/Lib_Js_Mage-1.6.1.0-rc1.xml b/var/package/Lib_Js_Mage-1.6.1.0.xml similarity index 94% rename from var/package/Lib_Js_Mage-1.6.1.0-rc1.xml rename to var/package/Lib_Js_Mage-1.6.1.0.xml index b951c93498..4b0a27ae91 100644 --- a/var/package/Lib_Js_Mage-1.6.1.0-rc1.xml +++ b/var/package/Lib_Js_Mage-1.6.1.0.xml @@ -1,18 +1,18 @@ Lib_Js_Mage - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable Mixed community Javascript Libraries for Magento Javascript Libraries for Magento - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - + 2011-10-19 + + - 5.2.06.0.0Lib_Js_Prototypecommunity1.7.0.01.7.1.0 + 5.2.06.0.0Lib_Js_Prototypecommunity1.7.0.0.21.7.1.0 diff --git a/var/package/Lib_Js_Prototype-1.7.0.0.2-rc1.xml b/var/package/Lib_Js_Prototype-1.7.0.0.2.xml similarity index 98% rename from var/package/Lib_Js_Prototype-1.7.0.0.2-rc1.xml rename to var/package/Lib_Js_Prototype-1.7.0.0.2.xml index 29efab2110..3555cc5193 100644 --- a/var/package/Lib_Js_Prototype-1.7.0.0.2-rc1.xml +++ b/var/package/Lib_Js_Prototype-1.7.0.0.2.xml @@ -1,17 +1,17 @@ Lib_Js_Prototype - 1.7.0.0.2-rc1 - beta + 1.7.0.0.2 + stable Mixed community Prototype and Scriptaculous Javascript Libraries for Magento Prototype and Scriptaculous Javascript Libraries for Magento - 1.7.0.0.2-rc1 + 1.7.0.0.2 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - + 2011-10-19 + 5.2.06.0.0 diff --git a/var/package/Lib_Js_TinyMCE-3.3.7.0.xml b/var/package/Lib_Js_TinyMCE-3.3.7.0.xml index b5974cf7c6..35644b2643 100644 --- a/var/package/Lib_Js_TinyMCE-3.3.7.0.xml +++ b/var/package/Lib_Js_TinyMCE-3.3.7.0.xml @@ -10,8 +10,8 @@ TinyMCE Javascript Libraries for Magento 3.3.7.0 Magento Core Teamcorecore@magentocommerce.com - 2011-07-11 - + 2011-07-07 + 5.2.06.0.0 diff --git a/var/package/Lib_LinLibertineFont-2.8.14.0.xml b/var/package/Lib_LinLibertineFont-2.8.14.0.xml index dbe748c610..99e6618acc 100644 --- a/var/package/Lib_LinLibertineFont-2.8.14.0.xml +++ b/var/package/Lib_LinLibertineFont-2.8.14.0.xml @@ -10,8 +10,8 @@ Libertine Open Fonts Project fonts for PDF print-outs 2.8.14.0 Magento Core Teamcorecore@magentocommerce.com - 2011-07-11 - + 2011-07-07 + 5.2.06.0.0 diff --git a/var/package/Lib_Mage-1.6.1.0-rc1.xml b/var/package/Lib_Mage-1.6.1.0.xml similarity index 97% rename from var/package/Lib_Mage-1.6.1.0-rc1.xml rename to var/package/Lib_Mage-1.6.1.0.xml index 0a6ea8f5c8..c4a00dc4e1 100644 --- a/var/package/Lib_Mage-1.6.1.0-rc1.xml +++ b/var/package/Lib_Mage-1.6.1.0.xml @@ -1,17 +1,17 @@ Lib_Mage - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable OSL v3.0 community Mage Library Mage Library - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - + 2011-10-19 + 5.2.06.0.0 diff --git a/var/package/Lib_Phpseclib-1.5.0.0.xml b/var/package/Lib_Phpseclib-1.5.0.0.xml index 61d4a388d2..484063b3b7 100644 --- a/var/package/Lib_Phpseclib-1.5.0.0.xml +++ b/var/package/Lib_Phpseclib-1.5.0.0.xml @@ -10,8 +10,8 @@ Phpseclib Library 1.5.0.0 Magento Core Teamcorecore@magentocommerce.com - 2011-07-11 - + 2011-07-07 + 5.2.06.0.0 diff --git a/var/package/Lib_Varien-1.6.1.0-rc1.xml b/var/package/Lib_Varien-1.6.1.0.xml similarity index 98% rename from var/package/Lib_Varien-1.6.1.0-rc1.xml rename to var/package/Lib_Varien-1.6.1.0.xml index 24a7636c36..6952290468 100644 --- a/var/package/Lib_Varien-1.6.1.0-rc1.xml +++ b/var/package/Lib_Varien-1.6.1.0.xml @@ -1,17 +1,17 @@ Lib_Varien - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable OSL v3.0 community Varien Library Varien Library - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - + 2011-10-19 + 5.2.06.0.0Lib_ZFcommunity1.11.1.01.12.0.0PDOSPLcurlSimpleXMLdomgdiconvpdo_mysqlmcryptpcreReflectionsession diff --git a/var/package/Lib_ZF-1.11.1.0.xml b/var/package/Lib_ZF-1.11.1.0.xml index f16497d690..12b6691e51 100644 --- a/var/package/Lib_ZF-1.11.1.0.xml +++ b/var/package/Lib_ZF-1.11.1.0.xml @@ -10,8 +10,8 @@ Zend Framework 1.11.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-06-15 - + 2011-07-07 + 5.2.06.0.0Lib_ZF_Localecommunity1.11.1.01.12.0.0 diff --git a/var/package/Lib_ZF_Locale-1.11.1.0.xml b/var/package/Lib_ZF_Locale-1.11.1.0.xml index b542af603b..add2eb1744 100644 --- a/var/package/Lib_ZF_Locale-1.11.1.0.xml +++ b/var/package/Lib_ZF_Locale-1.11.1.0.xml @@ -11,8 +11,8 @@ splitted from ZF to avoid memory overruns during installation and upgrades 1.11.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-06-15 - + 2011-07-07 + 5.2.06.0.0 diff --git a/var/package/Mage_All_Latest-1.6.1.0-rc1.xml b/var/package/Mage_All_Latest-1.6.1.0-rc1.xml deleted file mode 100644 index 3b1dbc8091..0000000000 --- a/var/package/Mage_All_Latest-1.6.1.0-rc1.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - Mage_All_Latest - 1.6.1.0-rc1 - beta - OSL v3.0 - community - - Metapackage for latest Magento 1.6.1.0-rc1 release - Metapackage for latest Magento 1.6.1.0-rc1 release - 1.6.1.0-rc1 - Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - - - 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.0-rc11.6.1.0-rc1Mage_Core_Adminhtmlcommunity1.6.1.0-rc11.6.1.0-rc1Interface_Adminhtml_Defaultcommunity1.6.1.0-rc11.6.1.0-rc1Interface_Frontend_Defaultcommunity1.6.0.01.6.1.0Interface_Install_Defaultcommunity1.6.0.01.6.1.0Mage_Downloadercommunity1.6.1.0-rc11.6.1.0-rc1Mage_Centinelcommunity1.6.1.0-rc11.6.1.0-rc1Interface_Frontend_Base_Defaultcommunity1.6.1.0-rc11.6.1.0-rc1Phoenix_Moneybookerscommunity1.3.01.4.0Mage_Compilercommunity1.6.0.01.6.1.0Magento_Mobilecommunity1.5.0.0.21.11.5.1.0 - diff --git a/var/package/Mage_All_Latest-1.6.1.0.xml b/var/package/Mage_All_Latest-1.6.1.0.xml new file mode 100644 index 0000000000..973e4f4733 --- /dev/null +++ b/var/package/Mage_All_Latest-1.6.1.0.xml @@ -0,0 +1,18 @@ + + + Mage_All_Latest + 1.6.1.0 + stable + OSL v3.0 + community + + Metapackage for latest Magento 1.6.1.0 release + Metapackage for latest Magento 1.6.1.0 release + 1.6.1.0 + Magento Core Teamcorecore@magentocommerce.com + 2011-10-19 + + + + 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.01.6.1.0Mage_Core_Adminhtmlcommunity1.6.1.01.6.1.0Interface_Adminhtml_Defaultcommunity1.6.1.01.6.1.0Interface_Frontend_Defaultcommunity1.6.0.01.6.0.0Interface_Install_Defaultcommunity1.6.1.01.6.1.0Mage_Downloadercommunity1.6.1.01.6.1.0Mage_Centinelcommunity1.6.1.01.6.1.0Interface_Frontend_Base_Defaultcommunity1.6.1.01.6.1.0Phoenix_Moneybookerscommunity1.3.01.4.0Mage_Compilercommunity1.6.0.01.6.0.0Magento_Mobilecommunity1.6.0.0.22.01.7.0.0 + diff --git a/var/package/Mage_Centinel-1.6.1.0-rc1.xml b/var/package/Mage_Centinel-1.6.1.0.xml similarity index 95% rename from var/package/Mage_Centinel-1.6.1.0-rc1.xml rename to var/package/Mage_Centinel-1.6.1.0.xml index 7a61551ca5..56ea6c9047 100644 --- a/var/package/Mage_Centinel-1.6.1.0-rc1.xml +++ b/var/package/Mage_Centinel-1.6.1.0.xml @@ -1,18 +1,18 @@ Mage_Centinel - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable mixed community 3D Secure Card Validation An integration with Cardinalcommerce Centinel service. Provides option to validate Visa and Mastercard cards for eliminating possible fraudlent order placement attempts. Adds information about Electronic Commerce Identifier, that designates liability for chargeback. - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - + 2011-10-19 + - 5.2.06.0.0Mage_Core_Modulescommunity1.6.0.01.6.1.0 + 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.01.7.0.0 diff --git a/var/package/Mage_Core_Adminhtml-1.6.1.0-rc1.xml b/var/package/Mage_Core_Adminhtml-1.6.1.0.xml similarity index 99% rename from var/package/Mage_Core_Adminhtml-1.6.1.0-rc1.xml rename to var/package/Mage_Core_Adminhtml-1.6.1.0.xml index f4500277be..89070e4a21 100644 --- a/var/package/Mage_Core_Adminhtml-1.6.1.0-rc1.xml +++ b/var/package/Mage_Core_Adminhtml-1.6.1.0.xml @@ -1,18 +1,18 @@ Mage_Core_Adminhtml - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable OSL v3.0 community Magento Administration Panel Magento Administration Panel - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - + 2011-10-19 + + - 5.2.06.0.0Mage_Core_Modulescommunity1.6.0.01.6.1.0Lib_Js_Calendarcommunity1.51.11.52Lib_Js_Extcommunity1.6.0.01.6.1.0Lib_LinLibertineFontcommunity2.8.14.02.9.0.0Lib_Js_TinyMCEcommunity3.3.7.03.3.8.0 + 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.01.7.0.0Lib_Js_Calendarcommunity1.51.11.52Lib_Js_Extcommunity1.6.0.01.7.0.0Lib_LinLibertineFontcommunity2.8.14.02.9.0.0Lib_Js_TinyMCEcommunity3.3.7.03.3.8.0 diff --git a/var/package/Mage_Core_Modules-1.6.1.0-rc1.xml b/var/package/Mage_Core_Modules-1.6.1.0-rc1.xml deleted file mode 100644 index 305aead5f9..0000000000 --- a/var/package/Mage_Core_Modules-1.6.1.0-rc1.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - Mage_Core_Modules - 1.6.1.0-rc1 - beta - OSL v3.0 - community - - Collection of Magento Core Modules - Collection of Magento Core Modules - 1.6.1.0-rc1 - Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - - - 5.2.06.0.0Lib_Variencommunity1.6.1.0-rc11.6.1.0Lib_Google_Checkoutcommunity1.5.0.01.5.1.0Lib_Js_Calendarcommunity1.51.11.52Lib_Js_Magecommunity1.6.1.0-rc11.6.1.0Lib_Js_Prototypecommunity1.7.0.0.2-rc11.7.1.0Lib_Phpseclibcommunity1.5.0.01.5.1.0Mage_Locale_en_UScommunity1.6.1.0-rc11.6.1.0Lib_Magecommunity1.6.1.0-rc11.6.1.0 - diff --git a/var/package/Mage_Core_Modules-1.6.1.0.xml b/var/package/Mage_Core_Modules-1.6.1.0.xml new file mode 100644 index 0000000000..8f5e9a0785 --- /dev/null +++ b/var/package/Mage_Core_Modules-1.6.1.0.xml @@ -0,0 +1,18 @@ + + + Mage_Core_Modules + 1.6.1.0 + stable + OSL v3.0 + community + + Collection of Magento Core Modules + Collection of Magento Core Modules + 1.6.1.0 + Magento Core Teamcorecore@magentocommerce.com + 2011-10-19 + + + + 5.2.06.0.0Lib_Variencommunity1.6.1.01.7.0.0Lib_Google_Checkoutcommunity1.5.0.01.5.1.0Lib_Js_Calendarcommunity1.51.11.52Lib_Js_Magecommunity1.6.1.01.7.0.0Lib_Js_Prototypecommunity1.7.0.0.21.7.1.0Lib_Phpseclibcommunity1.5.0.01.5.1.0Mage_Locale_en_UScommunity1.6.1.01.7.0.0Lib_Magecommunity1.6.1.01.7.0.0 + diff --git a/var/package/Mage_Downloader-1.6.1.0-rc1.xml b/var/package/Mage_Downloader-1.6.1.0.xml similarity index 98% rename from var/package/Mage_Downloader-1.6.1.0-rc1.xml rename to var/package/Mage_Downloader-1.6.1.0.xml index ee9b718c37..705e4358df 100644 --- a/var/package/Mage_Downloader-1.6.1.0-rc1.xml +++ b/var/package/Mage_Downloader-1.6.1.0.xml @@ -1,17 +1,17 @@ Mage_Downloader - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable OSL v3.0 community Magento Downloader Magento Downloader - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - + 2011-10-19 + 5.2.06.0.0 diff --git a/var/package/Mage_Locale_en_US-1.6.1.0-rc1.xml b/var/package/Mage_Locale_en_US-1.6.1.0.xml similarity index 90% rename from var/package/Mage_Locale_en_US-1.6.1.0-rc1.xml rename to var/package/Mage_Locale_en_US-1.6.1.0.xml index 4b53ad3b38..b25c99c955 100644 --- a/var/package/Mage_Locale_en_US-1.6.1.0-rc1.xml +++ b/var/package/Mage_Locale_en_US-1.6.1.0.xml @@ -1,18 +1,18 @@ Mage_Locale_en_US - 1.6.1.0-rc1 - beta + 1.6.1.0 + stable OSL v3.0 community en_US locale en_US locale - 1.6.1.0-rc1 + 1.6.1.0 Magento Core Teamcorecore@magentocommerce.com - 2011-09-29 - - + 2011-10-19 + + 5.2.06.0.0 diff --git a/var/package/Magento_Mobile-1.5.0.0.21.1.xml b/var/package/Magento_Mobile-1.5.0.0.21.1.xml deleted file mode 100644 index e52736428b..0000000000 --- a/var/package/Magento_Mobile-1.5.0.0.21.1.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - Magento_Mobile - 1.5.0.0.21.1 - stable - mixed - community - - Magento Mobile Xml Interface - An integration magento with mobile applications (e.g. iPhone, Android, iPad) - 1.5.0.0.21.1 version of package -internal svn revision #111730 - Magento Core Teamauto-convertedcore@magentocommerce.com - 2011-06-21 - - - - Mage_Core_Modulescommunity1.5.0.11.7.0.0 - diff --git a/var/package/Magento_Mobile-1.6.0.0.22.0.xml b/var/package/Magento_Mobile-1.6.0.0.22.0.xml new file mode 100644 index 0000000000..f898cb24b2 --- /dev/null +++ b/var/package/Magento_Mobile-1.6.0.0.22.0.xml @@ -0,0 +1,19 @@ + + + Magento_Mobile + 1.6.0.0.22.0 + stable + mixed + community + + Magento Mobile Xml Interface + An integration magento with mobile applications (e.g. iPhone, Android, iPad) + 1.6.0.0.22.0 version of package +internal svn revision #121425 + Magento Core Teamcorecore@magentocommerce.com + 2011-10-19 + + + + 5.2.06.0.0Mage_Core_Modulescommunity1.6.1.01.7.0.0 +