Classificação de atributos configuráveis ​​do produto Magento 1.9.1


24

Como eu já mencionei, parece haver um problema com o magento 1.9.1 e a classificação de atributos de produtos configuráveis. As opções de um produto configurável agora SEMPRE dependem do ID do produto simples. A ordem das opções de atributo é ignorada.

Voltei ao magento 1.9.0.1. Talvez alguém possa determinar como a classificação no 1.9.1 é feita. Seria ótimo para todos que usam produtos configuráveis ​​para corrigir isso.

Se alguém quiser ver isso, você pode fazê-lo aqui na loja de demonstração magento. Não consegui classificar os tamanhos corretamente.

Respostas:


25

Nota: Fui informado de que esta solução não funciona no Magento 1.9.2. Para economizar tempo desperdiçado por outras pessoas, eu gostaria de destacar isso na parte superior deste post. Se eu desenvolver minha própria solução ou encontrar a solução de outra pessoa que funcione na versão 1.9.2, atualizarei esta postagem naquele momento.

Nota: A solução apresentada aqui estende um arquivo de classe de bloco na biblioteca principal do Magento. Revisei o código fonte do Magento antes dessa abordagem e determinei que não havia um bom evento a ser observado para evitar essa abordagem. Se em uma versão futura do Magento esse problema de classificação for resolvido, você poderá desfazer essas alterações abaixo simplesmente desativando a extensão no arquivo XML do app / etc / modules.

Etapa 1: crie o arquivo app / etc / modules / FirstScribe_CatalogOptionSortFix.xml

Conteúdo:

<?xml version="1.0"?>
<config>
    <modules>
        <FirstScribe_CatalogOptionSortFix>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </FirstScribe_CatalogOptionSortFix>
    </modules>
</config>

Nota: Para as etapas 2 e 3, crie diretórios para esses arquivos, conforme necessário. Por exemplo, você já pode ter o diretório app / code / local ou não, dependendo das extensões que já instalou no site.

Etapa 2: Crie o arquivo app / code / local / FirstScribe / CatalogOptionSortFix / etc / config.xml

Conteúdo:

<?xml version="1.0"?>
<!--
/**
 * Magento 1.9.1.0 has a bug in that the configurable options are sorted by
 * ID rather than position for the Configurable Product's front end view script.
 * This extension addresses this problem.
 *
 * @category    FirstScribe
 * @package     FirstScribe_CatalogOptionSortFix
 * @version     2014.12.15
 */
-->
<config>
    <modules>
        <FirstScribe_CatalogOptionSortFix>
            <version>1.0.0</version>
        </FirstScribe_CatalogOptionSortFix>
    </modules>
    <global>
        <blocks>
            <catalog>
                <rewrite>
                    <product_view_type_configurable>FirstScribe_CatalogOptionSortFix_Block_Product_View_Type_Configurable</product_view_type_configurable>
                </rewrite>
            </catalog>
        </blocks>
    </global>
</config>

Etapa 3: Crie o arquivo app / code / local / FirstScribe / CatalogOptionSortFix / Block / Product / View / Type / Configurable.php

Conteúdo:

<?php
/**
 * Magento 1.9.1.0 has a bug in that the configurable options are sorted by
 * ID rather than position for the Configurable Product's front end view script.
 * This extension addresses this problem.
 *
 * @category    FirstScribe
 * @package     FirstScribe_CatalogOptionSortFix
 * @version     2014.12.15
 */
class FirstScribe_CatalogOptionSortFix_Block_Product_View_Type_Configurable extends Mage_Catalog_Block_Product_View_Type_Configurable
{
    /**
     * @var Magento_Db_Adapter_Pdo_Mysql
     */
    protected $_read;

    /**
     * @var string
     */
    protected $_tbl_eav_attribute_option;

    /**
     * Composes configuration for js
     *
     * @version 2014.12.15 - Addition of this line:
     *    $info['options'] = $this->_sortOptions($info['options']);
     *
     * @return string
     */
    public function getJsonConfig()
    {
        $attributes = array();
        $options    = array();
        $store      = $this->getCurrentStore();
        $taxHelper  = Mage::helper('tax');
        $currentProduct = $this->getProduct();

        $preconfiguredFlag = $currentProduct->hasPreconfiguredValues();
        if ($preconfiguredFlag) {
            $preconfiguredValues = $currentProduct->getPreconfiguredValues();
            $defaultValues       = array();
        }

        foreach ($this->getAllowProducts() as $product) {
            $productId  = $product->getId();

            foreach ($this->getAllowAttributes() as $attribute) {
                $productAttribute   = $attribute->getProductAttribute();
                $productAttributeId = $productAttribute->getId();
                $attributeValue     = $product->getData($productAttribute->getAttributeCode());
                if (!isset($options[$productAttributeId])) {
                    $options[$productAttributeId] = array();
                }

                if (!isset($options[$productAttributeId][$attributeValue])) {
                    $options[$productAttributeId][$attributeValue] = array();
                }
                $options[$productAttributeId][$attributeValue][] = $productId;
            }
        }

        $this->_resPrices = array(
            $this->_preparePrice($currentProduct->getFinalPrice())
        );

        foreach ($this->getAllowAttributes() as $attribute) {
            $productAttribute = $attribute->getProductAttribute();
            $attributeId = $productAttribute->getId();
            $info = array(
                    'id'        => $productAttribute->getId(),
                    'code'      => $productAttribute->getAttributeCode(),
                    'label'     => $attribute->getLabel(),
                    'options'   => array()
            );

            $optionPrices = array();
            $prices = $attribute->getPrices();
            if (is_array($prices)) {
                foreach ($prices as $value) {
                    if(!$this->_validateAttributeValue($attributeId, $value, $options)) {
                        continue;
                    }
                    $currentProduct->setConfigurablePrice(
                            $this->_preparePrice($value['pricing_value'], $value['is_percent'])
                    );
                    $currentProduct->setParentId(true);
                    Mage::dispatchEvent(
                            'catalog_product_type_configurable_price',
                            array('product' => $currentProduct)
                    );
                    $configurablePrice = $currentProduct->getConfigurablePrice();

                    if (isset($options[$attributeId][$value['value_index']])) {
                        $productsIndex = $options[$attributeId][$value['value_index']];
                    } else {
                        $productsIndex = array();
                    }

                    $info['options'][] = array(
                            'id'        => $value['value_index'],
                            'label'     => $value['label'],
                            'price'     => $configurablePrice,
                            'oldPrice'  => $this->_prepareOldPrice($value['pricing_value'], $value['is_percent']),
                            'products'  => $productsIndex,
                    );
                    $optionPrices[] = $configurablePrice;
                }
            }

            // CALL SORT ORDER FIX
            $info['options'] = $this->_sortOptions($info['options']);

            /**
             * Prepare formated values for options choose
             */
            foreach ($optionPrices as $optionPrice) {
                foreach ($optionPrices as $additional) {
                    $this->_preparePrice(abs($additional-$optionPrice));
                }
            }
            if($this->_validateAttributeInfo($info)) {
                $attributes[$attributeId] = $info;
            }

            // Add attribute default value (if set)
            if ($preconfiguredFlag) {
                $configValue = $preconfiguredValues->getData('super_attribute/' . $attributeId);
                if ($configValue) {
                    $defaultValues[$attributeId] = $configValue;
                }
            }
        }

        $taxCalculation = Mage::getSingleton('tax/calculation');
        if (!$taxCalculation->getCustomer() && Mage::registry('current_customer')) {
            $taxCalculation->setCustomer(Mage::registry('current_customer'));
        }

        $_request = $taxCalculation->getDefaultRateRequest();
        $_request->setProductClassId($currentProduct->getTaxClassId());
        $defaultTax = $taxCalculation->getRate($_request);

        $_request = $taxCalculation->getRateRequest();
        $_request->setProductClassId($currentProduct->getTaxClassId());
        $currentTax = $taxCalculation->getRate($_request);

        $taxConfig = array(
                'includeTax'        => $taxHelper->priceIncludesTax(),
                'showIncludeTax'    => $taxHelper->displayPriceIncludingTax(),
                'showBothPrices'    => $taxHelper->displayBothPrices(),
                'defaultTax'        => $defaultTax,
                'currentTax'        => $currentTax,
                'inclTaxTitle'      => Mage::helper('catalog')->__('Incl. Tax')
        );

        $config = array(
                'attributes'        => $attributes,
                'template'          => str_replace('%s', '#{price}', $store->getCurrentCurrency()->getOutputFormat()),
                'basePrice'         => $this->_registerJsPrice($this->_convertPrice($currentProduct->getFinalPrice())),
                'oldPrice'          => $this->_registerJsPrice($this->_convertPrice($currentProduct->getPrice())),
                'productId'         => $currentProduct->getId(),
                'chooseText'        => Mage::helper('catalog')->__('Choose an Option...'),
                'taxConfig'         => $taxConfig
        );

        if ($preconfiguredFlag && !empty($defaultValues)) {
            $config['defaultValues'] = $defaultValues;
        }

        $config = array_merge($config, $this->_getAdditionalConfig());    

        return Mage::helper('core')->jsonEncode($config);
    }

    /**
     * Sort the options based off their position.
     *
     * @param array $options
     * @return array
     */
    protected function _sortOptions($options)
    {
        if (count($options)) {
            if (!$this->_read || !$this->_tbl_eav_attribute_option) {
                $resource = Mage::getSingleton('core/resource');

                $this->_read = $resource->getConnection('core_read');
                $this->_tbl_eav_attribute_option = $resource->getTableName('eav_attribute_option');
            }

            // Gather the option_id for all our current options
            $option_ids = array();
            foreach ($options as $option) {
                $option_ids[] = $option['id'];

                $var_name  = 'option_id_'.$option['id'];
                $$var_name = $option;
            }

            $sql    = "SELECT `option_id` FROM `{$this->_tbl_eav_attribute_option}` WHERE `option_id` IN('".implode('\',\'', $option_ids)."') ORDER BY `sort_order`";
            $result = $this->_read->fetchCol($sql);

            $options = array();
            foreach ($result as $option_id) {
                $var_name  = 'option_id_'.$option_id;
                $options[] = $$var_name;
            }
        }

        return $options;
    }
}

Etapa 4: Se ativado, atualize o tipo de cache "Configuração" do Magento em Sistema -> Gerenciamento de cache do painel de administração.

Visão geral da extensão

  1. Estenda a classe Mage_Catalog_Block_Product_View_Type_Configurable.
  2. Adicione um método para classificar as opções por seu positionvalor, puxando essas informações do banco de dados.
  3. Reescreva o método getJsonConfig para chamar nossa nova função depois de reunir as opções para um atributo.

2
funciona de maneira fantástica e fico feliz que a solução não afete as atualizações futuras - muito obrigado por uma solução viável.
dawhoo

Hey @Meogi, embora pareça que sua correção seja perfeita para os valores dos atributos, temos certeza de que tudo está em ordem para as caixas de seleção do produto? Percebeu um problema ao ordená-los da maneira como são definidos no conjunto de atributos. Por exemplo - nós arrastamos "Cor" acima de "Tamanho" dentro do conjunto de atributos, no entanto, 1.9.1 trocou os dois (ignorou a ordem). A única maneira de corrigir isso era editar o produto em si e arrastar a ordem para dentro do configurável. Talvez este fosse apenas um produto não autorizado que foi acidentalmente reordenado manualmente anteriormente?
Joe

11
@ Joe Se não me engano, arrastar o atributo mais alto / mais baixo no conjunto de atributos não afeta a ordem em que são exibidos na página de detalhes do produto front-end. Em vez disso, você deve ir em Catálogo -> Atributos -> Gerenciar atributos, encontrar seu atributo e editar o valor "Posição". Isso afetará a ordem em que os atributos configuráveis ​​são exibidos na página do produto, bem como a navegação em camadas. A ordem das opções configuráveis ​​também pode ser substituída por produto, navegando até a guia "Produtos Associados" no administrador e arrastando os atributos para cima / para baixo.
Darren Felton

11
@Meogi é apenas para a posição do Bloco de Navegação em Camadas, para quando você ativar "Usar na Navegação em Camadas".
Joe

@ Joe vejo, bem, então eu não sei como alterar as configurações padrão (talvez sempre tenha sido o posicionamento no conjunto de atributos, não tenho certeza). Posso afirmar que em uma instalação do Magento 1.9.1.0 eu ainda era capaz de configurá-lo para uma ordem de minha escolha clicando / arrastando para cima / para baixo na guia "Produtos Associados" do produto configurável. Onde eles estão listados entre o formulário de criação rápida e a grade do produto na parte inferior.
Darren Felton

11

Apenas para adicionar meus dois centavos, as outras duas respostas foram boas para me apontar na direção da correção, mas pensei em atacá-la na fonte e não no ponto de apresentação do bloco.

Você pode obter o mesmo resultado estendendo o método Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collectiondo modelo _loadPrices(), que apesar do nome é o local onde uma alteração foi feita (presumivelmente por desempenho), resultando na ordenação dos atributos por ID e não por relevância.

A alteração parece ter sido feita para evitar foreachinstruções aninhadas , mas, por sua vez, também perde a ordem correta. Esta solução modifica ligeiramente a lógica atualizada para rastrear as opções de atributo e, em seguida, executa outro loop com base na ordem original para realmente adicionar.

Aqui está um passo a passo ajustado semelhante à resposta do meogi acima :


Etapa 1: registrar um novo módulo

Nota: se você já possui um, reutilize um existente.

# File: app/etc/modules/YourCompany_AttributeFix.xml
<?xml version="1.0"?>
<config>
    <modules>
        <YourCompany_AttributeFix>
            <active>true</active>
            <codePool>local</codePool>
            <depends>
                <Mage_Catalog />
            </depends>
        </YourCompany_AttributeFix>
    </modules>
</config>

Etapa 2: criar a configuração do módulo

# File: app/code/local/YourCompany/AttributeFix/etc/config.xml
<?xml version="1.0"?>
<config>
    <modules>
        <YourCompany_AttributeFix>
            <version>0.1.0</version>
        </YourCompany_AttributeFix>
    </modules>    
    <global>
        <models>
            <catalog_resource>
                <rewrite>
                    <product_type_configurable_attribute_collection>YourCompany_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_Collection</product_type_configurable_attribute_collection>
                </rewrite>
            </catalog_resource>
        </models>
    </global>
</config>

Etapa 3: adicionar a extensão do modelo de recurso

# File: app/code/local/YourCompany/AttributeFix/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
/**
 * Catalog Configurable Product Attribute Collection - overridden to re-enable the attribute option
 * sorting by relevance rather than by ID as changed in the Magento core class
 */
class YourCompany_AttributeFix_Model_Resource_Product_Type_Configurable_Attribute_Collection
    extends Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
{
    /**
     * Load attribute prices information
     *
     * @return Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
     */
    protected function _loadPrices()
    {
        if ($this->count()) {
            $pricings = array(
                0 => array()
            );

            if ($this->getHelper()->isPriceGlobal()) {
                $websiteId = 0;
            } else {
                $websiteId = (int)Mage::app()->getStore($this->getStoreId())->getWebsiteId();
                $pricing[$websiteId] = array();
            }

            $select = $this->getConnection()->select()
                ->from(array('price' => $this->_priceTable))
                ->where('price.product_super_attribute_id IN (?)', array_keys($this->_items));

            if ($websiteId > 0) {
                $select->where('price.website_id IN(?)', array(0, $websiteId));
            } else {
                $select->where('price.website_id = ?', 0);
            }

            $query = $this->getConnection()->query($select);

            while ($row = $query->fetch()) {
                $pricings[(int)$row['website_id']][] = $row;
            }

            $values = array();

            foreach ($this->_items as $item) {
                $productAttribute = $item->getProductAttribute();
                if (!($productAttribute instanceof Mage_Eav_Model_Entity_Attribute_Abstract)) {
                    continue;
                }
                $options = $productAttribute->getFrontend()->getSelectOptions();

                $optionsByValue = array();
                foreach ($options as $option) {
                    $optionsByValue[$option['value']] = $option['label'];
                }

                /**
                 * Modification to re-enable the sorting by relevance for attribute options
                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
                 */
                $toAdd = array();
                foreach ($this->getProduct()->getTypeInstance(true)
                             ->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
                         as $associatedProduct) {

                    $optionValue = $associatedProduct->getData($productAttribute->getAttributeCode());

                    if (array_key_exists($optionValue, $optionsByValue)) {
                        $toAdd[] = $optionValue;
                    }
                }

                // Add the attribute options, but in the relevant order rather than by ID
                foreach (array_intersect_key($optionsByValue, array_flip($toAdd)) as $optionValueKey => $optionValue) {
                    // If option available in associated product
                    if (!isset($values[$item->getId() . ':' . $optionValue])) {
                        // If option not added, we will add it.
                        $values[$item->getId() . ':' . $optionValueKey] = array(
                            'product_super_attribute_id' => $item->getId(),
                            'value_index'                => $optionValueKey,
                            'label'                      => $optionsByValue[$optionValueKey],
                            'default_label'              => $optionsByValue[$optionValueKey],
                            'store_label'                => $optionsByValue[$optionValueKey],
                            'is_percent'                 => 0,
                            'pricing_value'              => null,
                            'use_default_value'          => true
                        );
                    }
                }
                /**
                 * End attribute option order modification
                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
                 */
            }

            foreach ($pricings[0] as $pricing) {
                // Addding pricing to options
                $valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
                if (isset($values[$valueKey])) {
                    $values[$valueKey]['pricing_value']     = $pricing['pricing_value'];
                    $values[$valueKey]['is_percent']        = $pricing['is_percent'];
                    $values[$valueKey]['value_id']          = $pricing['value_id'];
                    $values[$valueKey]['use_default_value'] = true;
                }
            }

            if ($websiteId && isset($pricings[$websiteId])) {
                foreach ($pricings[$websiteId] as $pricing) {
                    $valueKey = $pricing['product_super_attribute_id'] . ':' . $pricing['value_index'];
                    if (isset($values[$valueKey])) {
                        $values[$valueKey]['pricing_value']     = $pricing['pricing_value'];
                        $values[$valueKey]['is_percent']        = $pricing['is_percent'];
                        $values[$valueKey]['value_id']          = $pricing['value_id'];
                        $values[$valueKey]['use_default_value'] = false;
                    }
                }
            }

            foreach ($values as $data) {
                $this->getItemById($data['product_super_attribute_id'])->addPrice($data);
            }
        }
        return $this;
    }
}

Etapa 4: limpe seu cache


Para referência , a mudança real na classe principal em git diffseria abaixo (não edite diretamente os arquivos principais!):

diff --git a/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php b/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
index 135d9d3..4d2a59b 100644
--- a/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
+++ b/app/code/core/Mage/Catalog/Model/Resource/Product/Type/Configurable/Attribute/Collection.php
@@ -254,6 +254,11 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
                     $optionsByValue[$option['value']] = $option['label'];
                 }

+                /**
+                 * Modification to re-enable the sorting by relevance for attribute options
+                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
+                 */
+                $toAdd = array();
                 foreach ($this->getProduct()->getTypeInstance(true)
                              ->getUsedProducts(array($productAttribute->getAttributeCode()), $this->getProduct())
                          as $associatedProduct) {
@@ -261,22 +266,31 @@ class Mage_Catalog_Model_Resource_Product_Type_Configurable_Attribute_Collection
                     $optionValue = $associatedProduct->getData($productAttribute->getAttributeCode());

                     if (array_key_exists($optionValue, $optionsByValue)) {
-                        // If option available in associated product
-                        if (!isset($values[$item->getId() . ':' . $optionValue])) {
-                            // If option not added, we will add it.
-                            $values[$item->getId() . ':' . $optionValue] = array(
-                                'product_super_attribute_id' => $item->getId(),
-                                'value_index'                => $optionValue,
-                                'label'                      => $optionsByValue[$optionValue],
-                                'default_label'              => $optionsByValue[$optionValue],
-                                'store_label'                => $optionsByValue[$optionValue],
-                                'is_percent'                 => 0,
-                                'pricing_value'              => null,
-                                'use_default_value'          => true
-                            );
-                        }
+                        $toAdd[] = $optionValue;
                     }
                 }
+
+                // Add the attribute options, but in the relevant order rather than by ID
+                foreach (array_intersect_key($optionsByValue, array_flip($toAdd)) as $optionValueKey => $optionValue) {
+                    // If option available in associated product
+                    if (!isset($values[$item->getId() . ':' . $optionValue])) {
+                        // If option not added, we will add it.
+                        $values[$item->getId() . ':' . $optionValueKey] = array(
+                            'product_super_attribute_id' => $item->getId(),
+                            'value_index'                => $optionValueKey,
+                            'label'                      => $optionsByValue[$optionValueKey],
+                            'default_label'              => $optionsByValue[$optionValueKey],
+                            'store_label'                => $optionsByValue[$optionValueKey],
+                            'is_percent'                 => 0,
+                            'pricing_value'              => null,
+                            'use_default_value'          => true
+                        );
+                    }
+                }
+                /**
+                 * End attribute option order modification
+                 * @author Robbie Averill <robbie.averill@kathmandu.co.nz>
+                 */
             }

             foreach ($pricings[0] as $pricing) {

Isso também está no GitHub se alguém quiser como referência.

Edit: Eu também registrei isso como um bug no Magento .


11
Contribuição impressionante meu amigo. +1 (sim, não deveria usar esses comentários a dizer obrigado, mas você o matou por isso tenho de haha)
Darren Felton

Eu tentei com o magento 1.9.2 - doenst parece funcionar infelizmente. E eu não entendo por que esse bug ainda não foi corrigido pelo Magento.
Reinsch

Você garantiu que seu módulo esteja configurado corretamente? Tenho certeza de que eles estão cientes disso, mas provavelmente é algo que levaria tempo para verificar antes de lançar um patch, pois é uma parte muito importante do sistema. Edit: Você também pode testar a correção diretamente (e temporariamente), mas copiar o patch diretamente na classe principal (temporariamente)
Robbie Averill

11
@ Reinsch Acabei de receber um email de alguém sobre incompatibilidade com o CE 1.9.2 - enviei uma atualização para o meu repositório Github e o testei no CE 1.9.2 com dados de exemplo Magento e está funcionando corretamente agora
Robbie Averill

11
Bom trabalho @RobbieAverill - muito obrigado. Testado e confirmado trabalhando em um site Magento 1.9.2.1.
Zigojacko

3

Esta não é realmente uma correção adequada, mas foi o que fiz temporariamente para evitar a necessidade de voltar à 1.9.0.1 até que o próximo lançamento do Magento esperançosamente resolva o problema corretamente. Ele classificará os valores das opções em ordem alfabética; é claro que você pode ordenar o que quiser, mas não sei como acessar a ordem de classificação definida no back-end e, alfabeticamente, é bom o suficiente para meus propósitos.

Mude o arquivo

/app/code/core/Mage/Catalog/Block/Product/View/Type/configurable.php

Alterar linha 215

if($this->_validateAttributeInfo($info)) {
   $attributes[$attributeId] = $info;
}

para

usort($info['options'], function ($a,$b)
    {
        return strcmp($a['label'],$b['label']);
    }
);
if($this->_validateAttributeInfo($info)) {
   $attributes[$attributeId] = $info;
}

2
Por favor, veja minha resposta para uma resposta que estenda adequadamente a biblioteca principal do Magento, em vez de modificá-la diretamente. Ainda assim, parabéns a Steve por essa resposta, pois me ajudou muito a saber por onde começar a desenvolver a solução que eu criei.
Darren Felton

Excelente trabalhou como charme no Enterprise mesmo graças uma tonelada você salvar meu dia ..
Bharat
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.