public function save(Product $product)
    {
        $flattened = $this->_flatten($product->getDetails());
        $this->_transaction->add("\n\t\t\tDELETE FROM\n\t\t\t\tproduct_detail\n\t\t\tWHERE\n\t\t\t\tproduct_id = :productID?i\n\t\t", ['productID' => $product->id]);
        foreach ($flattened as $detail) {
            $this->_transaction->add('
				INSERT INTO
					product_detail
					(
						product_id,
						name,
						value,
						value_int,
						locale
					)
				VALUES
					(
						:productID?i,
						:name?s,
						:value?s,
						:value?i,
						:locale?s
					)
				', array_merge($detail, ['productID' => $product->id]));
        }
        if (!$this->_transOverridden) {
            $this->_transaction->commit();
        }
    }
 /**
  * @param string | array $data              Product data submitted by form
  * @throws TransformationFailedException    Exception thrown if invalid type given
  *
  * @return Product                       Returns instance of Product object
  */
 public function reverseTransform($data)
 {
     $product = new Product($this->_locale, $this->_priceTypes, $this->_defaultCurrency, $this->_taxStrategy);
     $product->setName($data['name'])->setDisplayName($data['name'])->setBrand($data['brand'])->setCategory($data['category'])->setDescription($data['description'])->setType($this->_productTypes->get($data['type']));
     // setting prices
     foreach ($data['prices']['currencies'] as $currency => $typePrices) {
         foreach ($typePrices as $type => $price) {
             $product->setPrice($type, $currency, $price);
         }
     }
     // create the unit
     if (!empty($data['units'])) {
         foreach ($data['units'] as $unitData) {
             $unit = new Unit($this->_locale, $this->_priceTypes, $this->_defaultCurrency);
             $unit->setProduct($product);
             $unit->setSKU($unitData['sku']);
             $unit->setStockForLocation($unitData['stock'], $this->_defaultLocation);
             $unit->setVisible(true);
             $unit->revisionID = 1;
             foreach ($unitData['variants'] as $option) {
                 $unit->setOption($option['key'], $option['value']);
             }
             $product->addUnit($unit);
         }
     }
     return $product;
 }
 /**
  * Get the tax rate for an item to an address. If address is null,
  * a default address should be used.
  * 
  * @param  Product    $product The product to calulate tax for
  * @param  Address    $address The address delvered to
  * @return Collection          The tax rates to be applied
  * @throws AddressNotSetException If no address given or default address set
  */
 public function getProductTaxRates(Product $product, Address $address = null)
 {
     if ($address === null) {
         $address = $this->_defaultAddress;
     }
     if (!$address) {
         throw new \LogicException('No address given and no default address set. Either provide an address or ensure a default address is set on the loader.');
     }
     return $this->_taxResolver->getTaxRates($product->getType()->getName(), $address);
 }
 /**
  * Save data to product export table, create new row if not exists
  *
  * @throws \LogicException
  *
  * @return Edit
  */
 protected function _saveProductExport()
 {
     if (!$this->_product) {
         throw new \LogicException('Cannot edit product export info as no product is set');
     }
     $this->_trans->add("\n\t\t\tINSERT INTO\n\t\t\t\tproduct_export\n\t\t\t\t(\n\t\t\t\t\tproduct_id,\n\t\t\t\t\tlocale,\n\t\t\t\t\texport_value,\n\t\t\t\t\texport_description,\n\t\t\t\t\texport_manufacture_country_id,\n\t\t\t\t\texport_code\n\t\t\t\t)\n\t\t\tVALUES\n\t\t\t\t(\n\t\t\t\t\t:productID?i,\n\t\t\t\t\t:locale?sn,\n\t\t\t\t\t:exportValue?fn,\n\t\t\t\t\t:exportDescription?sn,\n\t\t\t\t\t:exportCountryID?s,\n\t\t\t\t\t:exportCode?sn\n\t\t\t\t)\n\t\t\tON DUPLICATE KEY UPDATE\n\t\t\t\texport_value\t\t\t\t\t= :exportValue?fn,\n\t\t\t\texport_description\t\t\t\t= :exportDescription?sn,\n\t\t\t\texport_manufacture_country_id\t= :exportCountryID?s,\n\t\t\t\texport_code                     = :exportCode?sn\n\t\t", ['productID' => $this->_product->id, 'locale' => $this->_locale->getID(), 'exportValue' => $this->_product->exportValue, 'exportDescription' => $this->_product->exportDescription, 'exportCountryID' => $this->_product->exportManufactureCountryID, 'exportCode' => $this->_product->getExportCode()]);
     return $this;
 }
 private function _setPrices(array $row)
 {
     $default = null;
     foreach ($this->_priceTypes as $type) {
         $price = $this->_getPriceObject($type, $row, $default);
         if (null === $default && $type === 'retail') {
             $default = $price;
         }
         $this->_product->getPrices()->add($price);
     }
 }
 public function build(Product\Product $product)
 {
     $this->setName('product-details-edit')->setAction($this->_container['routing.generator']->generate('ms.commerce.product.edit.attributes.action', ['productID' => $product->id]))->setMethod('post');
     $this->add('name', 'text', $this->_trans('ms.commerce.product.attributes.name.label'), ['data' => $product->name, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.name.help']])->val()->maxLength(255);
     $this->add('display_name', 'text', $this->_trans('ms.commerce.product.attributes.display-name.label'), ['data' => $product->displayName, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.display-name.help']])->val()->optional()->maxLength(255);
     $this->add('sort_name', 'text', $this->_trans('ms.commerce.product.attributes.sort-name.label'), ['data' => $product->sortName, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.sort-name.help']])->val()->optional()->maxLength(255);
     $this->add('category', 'datalist', $this->_trans('ms.commerce.product.attributes.category.label'), ['choices' => $this->_getCategories(), 'data' => $product->category, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.category.help']]);
     $this->add('brand', 'datalist', $this->_trans('ms.commerce.product.attributes.brand.label'), ['data' => $product->brand, 'choices' => $this->_getBrands(), 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.brand.help']])->val()->maxLength(255)->optional();
     $this->add('description', 'textarea', $this->_trans('ms.commerce.product.attributes.description.label'), ['data' => $product->description, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.description.help']])->val()->optional();
     $this->add('short_description', 'textarea', $this->_trans('ms.commerce.product.attributes.short-description.label'), ['data' => $product->shortDescription, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.short-description.help']])->val()->optional();
     $this->add('export_description', 'textarea', $this->_trans('ms.commerce.product.attributes.export-description.label'), ['data' => $product->exportDescription, 'attr' => ['data-help-key' => 'ms.commerce.product.attributes.export-description.help']])->val()->optional();
     $this->add('export_code', 'text', $this->_container['translator']->trans('ms.commerce.product.details.export-code.label'), ['data' => $product->getExportCode(), 'attr' => ['data-help-key' => 'ms.commerce.product.details.export-code.help']])->val()->optional();
     $this->add('supplier_ref', 'text', $this->_trans('ms.commerce.product.details.supplier-ref.label'), ['data' => $product->supplierRef, 'attr' => ['data-help-key' => 'ms.commerce.product.details.supplier-ref.help']])->val()->maxLength(255)->optional();
     $this->add('weight_grams', 'number', $this->_trans('ms.commerce.product.details.weight-grams.label'), ['data' => $product->weight, 'attr' => ['data-help-key' => 'ms.commerce.product.details.weight-grams.help']])->val()->number()->optional();
     $this->add('tags', 'textarea', $this->_trans('ms.commerce.product.details.tags.label'), ['data' => implode(', ', $product->tags), 'attr' => ['data-help-key' => 'ms.commerce.product.details.tags.help']])->val()->optional();
     $this->add('notes', 'textarea', $this->_trans('ms.commerce.product.details.notes.label'), ['data' => $product->notes, 'attr' => ['data-help-key' => 'ms.commerce.product.details.notes.help']])->val()->optional();
     $this->add('export_manufacture_country_id', 'choice', $this->_container['translator']->trans('ms.commerce.product.details.export-manufacture-country.label'), ['data' => $product->exportManufactureCountryID, 'choices' => $this->_container['country.list']->all(), 'attr' => ['data-help-key' => 'ms.commerce.product.details.export-manufacture-country.help']]);
     $typeChoices = [];
     foreach ($this->_container['product.types'] as $type) {
         $typeChoices[$type->getName()] = $type->getDisplayName();
     }
     $this->add('product_type', 'choice', $this->_container['translator']->trans('ms.commerce.product.details.product-type.label'), ['data' => $product->type->getName(), 'choices' => $typeChoices, 'attr' => ['data-help-key' => 'ms.commerce.product.details.product-type.help']]);
     return $this;
 }
    public function create(Product $product)
    {
        $result = $this->_query->run('INSERT INTO
				product
			SET

				product.type			= :type?s,
				product.name			= :name?s,
				product.weight_grams	= :weight?i,
				product.tax_rate		= :tax_rate?f,
				product.tax_strategy	= :tax_strat?s,
				product.supplier_ref    = :supplier?s,
				product.created_at		= :created_at?d,
				product.created_by		= :created_by?i,
				product.brand           = :brand?s,
				product.category        = :category?s
				', ['type' => $product->type->getName(), 'name' => $product->name, 'weight' => $product->weight, 'tax_rate' => $product->taxRate, 'tax_strat' => $this->_defaultTaxStrategy->getName(), 'supplier' => $product->supplierRef, 'created_at' => $product->authorship->createdAt(), 'created_by' => $product->authorship->createdBy()->id, 'brand' => $product->getBrand(), 'category' => $product->getCategory()]);
        $productID = $result->id();
        $info = $this->_query->run('INSERT INTO
				product_info
			SET
				product_id        = :id?i,
				locale            = :locale?s,
				display_name      = :displayName?s,
				sort_name         = :sortName?s,
				description       = :description?s,
				short_description = :shortDesc?s', ['id' => $productID, 'locale' => $this->_locale->getID(), 'displayName' => $product->displayName, 'sortName' => $product->sortName, 'description' => $product->description, 'shortDesc' => $product->shortDescription]);
        $queryAppend = [];
        $queryVars = [];
        foreach ($this->_priceTypes as $type) {
            foreach ($this->_currencyIDs as $currency) {
                $price = $product->getPrices()->exists($type) ? $product->getPrice($type, $currency) : 0;
                $queryAppend[] = "(?i, ?s, ?f, ?s, ?s)";
                $vars = [$productID, $type, $price, $currency, $this->_locale->getId()];
                $queryVars = array_merge($queryVars, $vars);
            }
        }
        $defaultPrices = $this->_query->run('INSERT INTO
				product_price (product_id, type, price, currency_id, locale)
			VALUES
				' . implode(', ', $queryAppend), $queryVars);
        $product->id = $productID;
        return $product;
    }
 protected function _getProductDetailsForm()
 {
     return $this->get('field.form')->generate($this->_product->getDetails(), ['action' => $this->generateUrl('ms.commerce.product.edit.details.action', ['productID' => $this->_product->id])]);
 }
 protected function _getDefaultValues(Product $product)
 {
     $defaultValues = $product->getDetails()->flatten();
     $defaultValues['title'] = !empty($defaultValues['title']) ? $defaultValues['title'] : $product->name;
     return $defaultValues;
 }
 public function getProductDisplayName(Product $product)
 {
     $name = $product->displayName ?: $product->name;
     return $product->getDetails()->author ? $product->getDetails()->author . ' - ' . $name : $name;
 }
 private function _getUnits(Product $product, array $options)
 {
     $units = [];
     $locations = $this->get('stock.locations');
     foreach ($product->getVisibleUnits() as $unit) {
         // Skip units that don't meet the options criteria, if set
         if ($options && $options !== array_intersect_assoc($options, $unit->options)) {
             continue;
         }
         if (1 > $unit->getStockForLocation($locations->getRoleLocation($locations::SELL_ROLE))) {
             $this->_outOfStock[(int) $unit->id] = $unit->id;
         }
         $units[$unit->id] = $unit;
     }
     return $units;
 }
 public function getTaxRates()
 {
     $this->_load('taxes');
     return parent::getTaxRates();
 }
 protected function _getAvailableUnits(Product $product, array $options = [])
 {
     $key = md5(serialize(array($product->id, $options)));
     if (!array_key_exists($key, $this->_availableUnits)) {
         $this->_availableUnits[$key] = [];
         foreach ($product->getVisibleUnits() as $unit) {
             // Skip units that don't meet the options criteria, if set
             if ($options && $options !== array_intersect_assoc($options, $unit->options)) {
                 continue;
             }
             $this->_availableUnits[$key][$unit->id] = $unit;
         }
     }
     return $this->_availableUnits[$key];
 }