/**
  * {@inheritdoc}
  */
 public function submitForm(array &$form, FormStateInterface $form_state)
 {
     foreach (array_filter($form_state->getValue('add_attributes')) as $aid) {
         // Enable all options for added attributes.
         $attribute = uc_attribute_load($aid);
         $oid = 0;
         if (isset($attribute->options)) {
             foreach ($attribute->options as $option) {
                 $option->{$this->idField} = $this->idValue;
                 unset($option->name);
                 unset($option->aid);
                 db_insert($this->optionTable)->fields((array) $option)->execute();
             }
             // Make the first option (if any) the default.
             if ($option = reset($attribute->options)) {
                 $oid = $option->oid;
             }
         }
         $select = db_select('uc_attributes', 'a')->condition('aid', $aid);
         $select->addExpression(':id', $this->idField, array(':id' => $this->idValue));
         $select->addField('a', 'aid');
         $select->addField('a', 'label');
         $select->addField('a', 'ordering');
         $select->addExpression(':oid', 'default_option', array(':oid' => $oid));
         $select->addField('a', 'required');
         $select->addField('a', 'display');
         db_insert($this->attributeTable)->from($select)->execute();
     }
     $num = count(array_filter($form_state->getValue('add_attributes')));
     if ($num > 0) {
         $this->attributesAdded();
         drupal_set_message($this->formatPlural($num, '1 attribute has been added.', '@count attributes have been added.'));
     }
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, $attributes = NULL)
 {
     $form['attributes']['#tree'] = TRUE;
     foreach ($attributes as $aid => $attribute) {
         $base_attr = uc_attribute_load($aid);
         if ($base_attr->options) {
             $form['attributes'][$aid]['options'] = array('#type' => 'table', '#header' => array($this->t('Options'), $this->t('Default'), $this->t('Cost'), $this->t('Price'), $this->t('Weight'), $this->t('List position')), '#caption' => '<h2>' . SafeMarkup::checkPlain($attribute->name) . '</h2>', '#empty' => $this->t('This attribute does not have any options.'), '#tabledrag' => array(array('action' => 'order', 'relationship' => 'sibling', 'group' => 'uc-attribute-option-table-ordering')));
             $query = db_select('uc_attribute_options', 'ao')->fields('ao', array('aid', 'oid', 'name'));
             $query->leftJoin($this->optionTable, 'po', "ao.oid = po.oid AND po." . $this->idField . " = :id", array(':id' => $this->idValue));
             $query->addField('ao', 'cost', 'default_cost');
             $query->addField('ao', 'price', 'default_price');
             $query->addField('ao', 'weight', 'default_weight');
             $query->addField('ao', 'ordering', 'default_ordering');
             $query->fields('po', array('cost', 'price', 'weight', 'ordering'))->addExpression('CASE WHEN po.ordering IS NULL THEN 1 ELSE 0 END', 'null_order');
             $query->condition('aid', $aid)->orderBy('null_order')->orderBy('po.ordering')->orderBy('default_ordering')->orderBy('ao.name');
             $result = $query->execute();
             foreach ($result as $option) {
                 $oid = $option->oid;
                 $form['attributes'][$aid]['options'][$oid]['#attributes']['class'][] = 'draggable';
                 $form['attributes'][$aid]['options'][$oid]['select'] = array('#type' => 'checkbox', '#title' => SafeMarkup::checkPlain($option->name), '#default_value' => isset($attribute->options[$oid]));
                 $form['attributes'][$aid]['options'][$oid]['default'] = array('#type' => 'radio', '#title' => $this->t('Default'), '#title_display' => 'invisible', '#parents' => array('attributes', $aid, 'default'), '#return_value' => $oid, '#default_value' => $attribute->default_option);
                 $form['attributes'][$aid]['options'][$oid]['cost'] = array('#type' => 'uc_price', '#title' => $this->t('Cost'), '#title_display' => 'invisible', '#default_value' => is_null($option->cost) ? $option->default_cost : $option->cost, '#size' => 6, '#allow_negative' => TRUE);
                 $form['attributes'][$aid]['options'][$oid]['price'] = array('#type' => 'uc_price', '#title' => $this->t('Price'), '#title_display' => 'invisible', '#default_value' => is_null($option->price) ? $option->default_price : $option->price, '#size' => 6, '#allow_negative' => TRUE);
                 $form['attributes'][$aid]['options'][$oid]['weight'] = array('#type' => 'textfield', '#title' => $this->t('Weight'), '#title_display' => 'invisible', '#default_value' => is_null($option->weight) ? $option->default_weight : $option->weight, '#size' => 5);
                 $form['attributes'][$aid]['options'][$oid]['ordering'] = array('#type' => 'weight', '#title' => $this->t('List position'), '#title_display' => 'invisible', '#delta' => 50, '#default_value' => is_null($option->ordering) ? $option->default_ordering : $option->ordering, '#attributes' => array('class' => array('uc-attribute-option-table-ordering')));
             }
         }
     }
     $form['actions'] = array('#type' => 'actions');
     $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save changes'));
     return $form;
 }
/**
 * Make alterations to a specific variant of a product node.
 *
 * @param $node
 *   The product node to be altered.
 */
function hook_uc_product_alter(&$node)
{
    if (isset($node->data['attributes']) && is_array($node->data['attributes'])) {
        $options = _uc_cart_product_get_options($node);
        foreach ($options as $option) {
            $node->cost += $option['cost'];
            $node->price += $option['price'];
            $node->weight += $option['weight'];
        }
        $combination = array();
        foreach ($node->data['attributes'] as $aid => $value) {
            if (is_numeric($value)) {
                $attribute = uc_attribute_load($aid, $node->nid, 'product');
                if ($attribute && ($attribute->display == 1 || $attribute->display == 2)) {
                    $combination[$aid] = $value;
                }
            }
        }
        ksort($combination);
        $model = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = :nid AND combination LIKE :combo", array(':nid' => $node->nid, ':combo' => serialize($combination)))->fetchField();
        if (!empty($model)) {
            $node->model = $model;
        }
    }
}
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, $aid = NULL)
 {
     $attribute = uc_attribute_load($aid);
     $form = parent::buildForm($form, $form_state, $aid);
     $form['#title'] = $this->t('Options for %name', ['%name' => $attribute->name]);
     return $form;
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, $aid = NULL)
 {
     $attribute = uc_attribute_load($aid);
     $form = parent::buildForm($form, $form_state);
     $form['#title'] = $this->t('Edit attribute: %name', ['%name' => $attribute->name]);
     $form['aid'] = array('#type' => 'value', '#value' => $attribute->aid);
     $form['name']['#default_value'] = $attribute->name;
     $form['label']['#default_value'] = $attribute->label ?: $attribute->name;
     $form['description']['#default_value'] = $attribute->description;
     $form['required']['#default_value'] = $attribute->required;
     $form['display']['#default_value'] = $attribute->display;
     $form['ordering']['#default_value'] = $attribute->ordering;
     return $form;
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, $aid = NULL)
 {
     $attribute = uc_attribute_load($aid);
     $form['#title'] = $this->t('Options for %name', ['%name' => $attribute->name]);
     $form['options'] = array('#type' => 'table', '#header' => array($this->t('Name'), $this->t('Default cost'), $this->t('Default price'), $this->t('Default weight'), $this->t('List position'), $this->t('Operations')), '#empty' => $this->t('No options for this attribute have been added yet.'), '#tabledrag' => array(array('action' => 'order', 'relationship' => 'sibling', 'group' => 'uc-attribute-option-table-ordering')));
     foreach ($attribute->options as $oid => $option) {
         $form['options'][$oid]['#attributes']['class'][] = 'draggable';
         $form['options'][$oid]['name'] = array('#markup' => $option->name);
         $form['options'][$oid]['cost'] = array('#theme' => 'uc_price', '#price' => $option->cost);
         $form['options'][$oid]['price'] = array('#theme' => 'uc_price', '#price' => $option->price);
         $form['options'][$oid]['weight'] = array('#markup' => (string) $option->weight);
         $form['options'][$oid]['ordering'] = array('#type' => 'weight', '#title' => $this->t('List position'), '#title_display' => 'invisible', '#default_value' => $option->ordering, '#attributes' => array('class' => array('uc-attribute-option-table-ordering')));
         $form['options'][$oid]['operations'] = array('#type' => 'operations', '#links' => array('edit' => array('title' => $this->t('Edit'), 'url' => Url::fromRoute('uc_attribute.option_edit', ['aid' => $attribute->aid, 'oid' => $oid])), 'delete' => array('title' => $this->t('Delete'), 'url' => Url::fromRoute('uc_attribute.option_delete', ['aid' => $attribute->aid, 'oid' => $oid]))));
     }
     $form['actions'] = array('#type' => 'actions');
     $form['actions']['submit'] = array('#type' => 'submit', '#value' => $this->t('Save changes'));
     return $form;
 }
 /**
  * {@inheritdoc}
  */
 public function buildForm(array $form, FormStateInterface $form_state, $aid = NULL)
 {
     $this->attribute = uc_attribute_load($aid);
     return parent::buildForm($form, $form_state);
 }
 /**
  * Tests that product in cart has the selected attribute option.
  */
 public function testAttributeAddToCart()
 {
     for ($display = 0; $display <= 3; ++$display) {
         // Set up an attribute.
         $data = array('display' => $display);
         $attribute = $this->createAttribute($data);
         if ($display) {
             // Give the attribute an option.
             $option = $this->createAttributeOption(array('aid' => $attribute->aid));
         }
         $attribute = uc_attribute_load($attribute->aid);
         // Put the attribute on a product.
         $product = $this->createProduct();
         uc_attribute_subject_save($attribute, 'product', $product->id(), TRUE);
         // Add the product to the cart.
         if ($display == 3) {
             $edit = array("attributes[{$attribute->aid}][{$option->oid}]" => $option->oid);
         } elseif (isset($option)) {
             $edit = array("attributes[{$attribute->aid}]" => $option->oid);
         } else {
             $option = new \stdClass();
             $option->name = self::randomMachineName();
             $option->price = 0;
             $edit = array("attributes[{$attribute->aid}]" => $option->name);
         }
         $this->addToCart($product, $edit);
         $this->assertText("{$attribute->label}: {$option->name}", 'Option selected on cart item.');
         $this->assertText(uc_currency_format($product->price->value + $option->price), 'Product has adjusted price.');
     }
 }
 /**
  * Creates a product with all attribute types and options.
  *
  * @param $product_class
  *   Defaults to FALSE to create a normal product, set to TRUE to
  *   create a product class instead.
  */
 protected function createCartLinksProduct($product_class = FALSE)
 {
     // Create a product
     if ($product_class) {
         $product = $this->createProductClass(array('promote' => 0));
     } else {
         $product = $this->createProduct(array('promote' => 0));
     }
     // Create some attributes
     for ($i = 0; $i < 5; $i++) {
         $attribute = $this->createAttribute();
         $attributes[$attribute->aid] = $attribute;
     }
     // Add some options, organizing them by aid and oid.
     $attribute_aids = array_keys($attributes);
     $all_options = array();
     foreach ($attribute_aids as $aid) {
         for ($i = 0; $i < 3; $i++) {
             $option = $this->createAttributeOption(array('aid' => $aid));
             $all_options[$option->aid][$option->oid] = $option;
         }
     }
     // array('required' => TRUE)
     // Get the options.
     $attribute = uc_attribute_load($attribute->aid);
     // Load every attribute we got.
     $attributes_with_options = uc_attribute_load_multiple();
     // Pick 5 keys to check at random.
     $aids = array_rand($attributes, 3);
     // Load the attributes back.
     $loaded_attributes = uc_attribute_load_multiple($aids);
     // TODO: add attributes of all 4 types
     // TODO: create both required and not required attributes
     // Add the selected attributes to the product.
     foreach ($loaded_attributes as $loaded_attribute) {
         uc_attribute_subject_save($loaded_attribute, 'product', $product->id(), TRUE);
     }
     return $product;
 }
 public function testProductKitAttributes()
 {
     $this->drupalLogin($this->adminUser);
     // Create a 20% inclusive tax rate.
     $rate = (object) array('name' => $this->randomMachineName(8), 'rate' => 0.2, 'taxed_product_types' => array('product'), 'taxed_line_items' => [], 'weight' => 0, 'shippable' => 0, 'display_include' => 1, 'inclusion_text' => $this->randomMachineName(6));
     uc_tax_rate_save($rate);
     // Ensure Rules picks up the new condition.
     // entity_flush_caches();
     // Create a $10 product.
     $product = $this->createProduct(array('price' => 10));
     // Create an attribute.
     $attribute = (object) array('name' => $this->randomMachineName(8), 'label' => $this->randomMachineName(8), 'description' => $this->randomMachineName(8), 'required' => TRUE, 'display' => 1, 'ordering' => 0);
     uc_attribute_save($attribute);
     // Create an option with a price adjustment of $5.
     $option = (object) array('aid' => $attribute->aid, 'name' => $this->randomMachineName(8), 'cost' => 0, 'price' => 5, 'weight' => 0, 'ordering' => 0);
     uc_attribute_option_save($option);
     // Attach the attribute to the product.
     $attribute = uc_attribute_load($attribute->aid);
     uc_attribute_subject_save($attribute, 'product', $product->id(), TRUE);
     // Create a product kit containing the product.
     $kit = $this->drupalCreateNode(array('type' => 'product_kit', 'products' => array($product->id()), 'default_qty' => 1, 'mutable' => UC_PRODUCT_KIT_UNMUTABLE_WITH_LIST));
     // Set the kit total to $9 to automatically apply a discount.
     $kit = node_load($kit->id());
     $kit->kit_total = 9;
     $kit->save();
     $kit = node_load($kit->id());
     $this->assertEqual($kit->products[$product->id()]->discount, -1, 'Product kit component has correct discount applied.');
     // Ensure the price is displayed tax-inclusively on the add-to-cart form.
     $this->drupalGet('node/' . $kit->id());
     $this->assertText('$10.80' . $rate->inclusion_text, 'Tax inclusive price on node-view form is accurate.');
     // $10.80 = $9.00 + 20%
     $this->assertRaw($option->name . ', +$6.00</option>', 'Tax inclusive option price on node view form is accurate.');
     // $6.00 = $5.00 + 20%
     // Add the product kit to the cart, selecting the option.
     $attribute_key = 'products[' . $product->id() . '][attributes][' . $attribute->aid . ']';
     $this->addToCart($kit, array($attribute_key => $option->oid));
     // Check that the subtotal is $16.80 ($10 base + $5 option - $1 discount, with 20% tax)
     $this->drupalGet('cart');
     $this->assertText('Subtotal: $16.80', 'Order subtotal is correct on cart page.');
     // @todo: disable rest of test, see [#2306379]
     return;
     // Make sure that the subtotal is also correct on the checkout page.
     $this->drupalPostForm('cart', [], 'Checkout');
     $this->assertText('Subtotal: $16.80', 'Order subtotal is correct on checkout page.');
     // Manually proceed to checkout review.
     $edit = $this->populateCheckoutForm();
     $this->drupalPostForm('cart/checkout', $edit, t('Review order'));
     $this->assertRaw(t('Your order is almost complete.'));
     // Make sure the price is still listed tax-inclusively.
     // @TODO This could be handled more specifically with a regex.
     $this->assertText('$16.80' . $rate->inclusion_text, 'Tax inclusive price appears in cart pane on checkout review page');
     // Ensure the tax-inclusive price is listed on the order admin page.
     $order_ids = \Drupal::entityQuery('uc_order')->condition('delivery_first_name', $edit['panes[delivery][first_name]'])->execute();
     $order_id = reset($order_ids);
     $this->assertTrue($order_id, 'Order was created successfully');
     $this->drupalGet('admin/store/orders/' . $order_id);
     $this->assertText('$16.80' . $rate->inclusion_text, 'Tax inclusive price appears on the order view page.');
     // And on the invoice.
     $this->drupalGet('admin/store/orders/' . $order_id . '/invoice');
     $this->assertText('$16.80' . $rate->inclusion_text, 'Tax inclusive price appears on the invoice.');
     // And on the printable invoice.
     $this->drupalGet('admin/store/orders/' . $order_id . '/invoice');
     $this->assertText('$16.80' . $rate->inclusion_text, 'Tax inclusive price appears on the printable invoice.');
 }