<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>NemoPS &#187; virtual products</title>
	<atom:link href="https://nemops.com/tag/virtual-products/feed/" rel="self" type="application/rss+xml" />
	<link>https://nemops.com</link>
	<description>Prestashop Tutorials, Modules and More!</description>
	<lastBuildDate>Wed, 05 Dec 2018 13:25:23 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>https://wordpress.org/?v=4.1.35</generator>
	<item>
		<title>Specific payment methods for virtual products in Prestashop</title>
		<link>https://nemops.com/prestashop-virtual-products-specific-payment/</link>
		<comments>https://nemops.com/prestashop-virtual-products-specific-payment/#comments</comments>
		<pubDate>Tue, 07 Apr 2015 09:39:48 +0000</pubDate>
		<dc:creator><![CDATA[Nemo]]></dc:creator>
				<category><![CDATA[Blog]]></category>
		<category><![CDATA[downloads]]></category>
		<category><![CDATA[payment]]></category>
		<category><![CDATA[virtual products]]></category>

		<guid isPermaLink="false">http://nemops.com/?p=2364</guid>
		<description><![CDATA[<p>In this quick tip we will see how to enable or disable payment methods for virtual products in Prestashop If you are selling both virtual and normal products, it might be worth displaying your customers specific payment methods depending on what they&#8217;re buying. It&#8217;s useless, for example, to allow them to pay by cash on [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://nemops.com/prestashop-virtual-products-specific-payment/">Specific payment methods for virtual products in Prestashop</a> appeared first on <a rel="nofollow" href="https://nemops.com">NemoPS</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>In this quick tip we will see how to enable or disable payment methods for virtual products in Prestashop</p>
<p><span id="more-2364"></span></p>
<p>If you are selling both virtual and normal products, it might be worth displaying your customers specific payment methods depending on what they&#8217;re buying. It&#8217;s useless, for example, to allow them to pay by cash on delivery something they can download soon after purchase! Similarly, you might want to offer a specific digital payment for virtual products only.</p>
<p>It is important to know that the modification we are about to apply <strong>needs to be made on every single payment module you want to restrict</strong>. This means having to hard-modify core files on older prestashop versions, or using overrides on newer than 1.6.0.11. For simplicity, I will modify core files, but have a look at my tutorial on <a href="http://nemops.com/override-prestashop-modules-core/#.VRlOVfnQqr0" target="_blank" title="how to override Prestashop Modules' core files">how to override Prestashop Modules&#8217; core files</a>.</p>
<div class="separator"></div>
<h2>The hookPayment method</h2>
<p>The simplest way to restrict our payment methods is to modify the method named <strong>hookPayment</strong>, used by every single Prestashop Payment module. It&#8217;s the function responsible of displaying payment methods during the checkout, so hiding them here will prevent people from continuing the purchase with them.</p>
<p>We will use bankwire as an example, but any other will equally do. Since non-standard modules might have their own rules, it&#8217;s better to add these lines of code at the very beginning of the method.</p>
<p>Therefore, open up <strong>modules/bankwire/bankwire.php</strong> (or use overrides), and scroll to the <strong>hookPayment</strong> method, which looks like this:</p>
<pre class="brush: php; title: ; notranslate">

	public function hookPayment($params)
	{

		if (!$this-&gt;active)
			return;
		if (!$this-&gt;checkCurrency($params['cart']))
			return;

		$this-&gt;smarty-&gt;assign(array(
			'this_path' =&gt; $this-&gt;_path,
			'this_path_bw' =&gt; $this-&gt;_path,
			'this_path_ssl' =&gt; Tools::getShopDomainSsl(true, true).__PS_BASE_URI__.'modules/'.$this-&gt;name.'/'
		));
		return $this-&gt;display(__FILE__, 'payment.tpl');
	}

</pre>
<p>First of all, we need to check which products are in the cart</p>
<pre class="brush: php; title: ; notranslate">
$products = $this-&gt;context-&gt;cart-&gt;getProducts();
</pre>
<p>Then, we loop through them, and see if any is virtual:</p>
<pre class="brush: php; title: ; notranslate">
		$products = $this-&gt;context-&gt;cart-&gt;getProducts();

		if($products)
		{
			foreach ($products as $product) {
				if ($product['is_virtual'])
				{
					// what to do?
				}		
			}
		}
</pre>
<p>It&#8217;s dead easy, isn&#8217;t it? Now we can choose what to do, let&#8217;s try removing bankwire for virtual products!</p>
<pre class="brush: php; title: ; notranslate">
		$products = $this-&gt;context-&gt;cart-&gt;getProducts();

		if($products)
		{
			foreach ($products as $product) {
				if ($product['is_virtual'])
				{
					return false;
				}		
			}
		}
</pre>
<p>What happens now? This will kill the bankwire payment method whenever someone has a virtual product in the cart. It means, of course that it won&#8217;t be accessible even if there are other products there. This might lead to unwanted situations, when no method is available at all.</p>
<p>For example, you have two methods: bankwire, and paypal. You remove bankwire with this method on virtual products, and paypal on others. What happens when a user has both a virtual and normal product in the cart? <strong>No method will be available!</strong> We therefore need to make a choice, whether to leave a warning in the cart page (shopping-cart.tpl) or explore another way.</p>
<p>We might want to only hide the method if <strong>all</strong> products are virtual, for example. How to?</p>
<p>The easiest way is to use $this->context->cart->isVirtualCart(), which will return true if all products in it are virtual. Alternatively, we can do it manually ourselves: </p>
<pre class="brush: php; title: ; notranslate">
	if($products)
	{
		$virtuals = 0;
		foreach ($products as $product) {
			if ($product['is_virtual'])
			{
				$virtuals++;
			}		
		}
		if($virtuals == count($products))
			return false;
	}
</pre>
<p>In this case, we use a counter to check how many virtual products we have. If all of them are virtual, we disable the module! How can we revere it then?</p>
<pre class="brush: php; title: ; notranslate">
		$products = $this-&gt;context-&gt;cart-&gt;getProducts();

		if($products)
		{
			foreach ($products as $product) {
				if (!$product['is_virtual'])
				{
					return false;
				}		
			}
		}
</pre>
<p>This will disable bankwire whenever a non-virtual product is in the cart.</p>
<pre class="brush: php; title: ; notranslate">
	if($products)
	{
		$nonvirtuals = 0;
		foreach ($products as $product) {
			if (!$product['is_virtual'])
			{
				$nonvirtuals++;
			}		
		}
		if($nonvirtuals == count($products))
			return false;
	}
</pre>
<p>While this other will hide it when all products are physical.</p>
<div class="separator"></div>
<h2>Conclusion</h2>
<p>There are many ways to utilize the restriction, depending on your needs (such as even buying a certain number of products!). Just keep in mind that you might end up not having any payment available if you mix up the wrong conditions!</p>
<p>The post <a rel="nofollow" href="https://nemops.com/prestashop-virtual-products-specific-payment/">Specific payment methods for virtual products in Prestashop</a> appeared first on <a rel="nofollow" href="https://nemops.com">NemoPS</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://nemops.com/prestashop-virtual-products-specific-payment/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Enable Combinations for Virtual Products in Prestashop 1.6/1.5</title>
		<link>https://nemops.com/prestashop-virtual-products-combinations/</link>
		<comments>https://nemops.com/prestashop-virtual-products-combinations/#comments</comments>
		<pubDate>Tue, 14 Jan 2014 09:51:03 +0000</pubDate>
		<dc:creator><![CDATA[Nemo]]></dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[combinations]]></category>
		<category><![CDATA[prestashop]]></category>
		<category><![CDATA[virtual products]]></category>

		<guid isPermaLink="false">http://nemops.com/?p=1767</guid>
		<description><![CDATA[<p>Prestashop 1.4 used to let you create combinations for downloadable items. It was the same file for every combinations, but we could use them, at least. This is no longer possible with Prestashop 1.5 and 1.6&#8217;s virtual products, so let&#8217;s see how to turn this feature back! The problem: You cannot use combinations with a [&#8230;]</p>
<p>The post <a rel="nofollow" href="https://nemops.com/prestashop-virtual-products-combinations/">Enable Combinations for Virtual Products in Prestashop 1.6/1.5</a> appeared first on <a rel="nofollow" href="https://nemops.com">NemoPS</a>.</p>
]]></description>
				<content:encoded><![CDATA[<p>Prestashop 1.4 used to let you create combinations for downloadable items. It was the same file for every combinations, but we could use them, at least. This is no longer possible with Prestashop 1.5 and 1.6&#8217;s virtual products, so let&#8217;s see how to turn this feature back!</p>
<p><span id="more-1767"></span></p>
<h2>The problem: You cannot use combinations with a virtual product</h2>
<p>If you ever tried to add attribute combinations to a virtual product, or turn into virtual a normal one that already had them, you might have run into this message. In the worst case, you just upgraded from 1.4 and discovered your products didn&#8217;t have the previous files attached anymore, if they had combinations. Despite having to re-bind all the downloadable files in the latter case, we can re-enable combinations for virtual products in Prestashop 1.5 using some overrides:</p>
<ul>
<li>AdminProductsController</li>
<li>combinations.tpl (back office template file)</li>
<li>virtualproduct.tpl</li>
</ul>
<p>We will simply disable a couple of checks Prestashop does, which turn off combinations if the product is virtual, and vice-versa.</p>
<div class="separator"></div>
<h2>The AdminProductsController Override</h2>
<p>First of all, let&#8217;s disable the virtual product check in the product&#8217;s back office. Create a new file in <em>override/controllers/admin</em> and call it <strong>AdminProductsController.php</strong>. Add the following to begin with, inside php tags of course:</p>
<pre class="brush: php; title: ; notranslate">
class AdminProductsController extends AdminProductsControllerCore
{
}
</pre>
<p>At this point, we need to extend and modify the method named <strong>initFormAttributes</strong>. Therefore, open up the original AdminProductsController.php (<em>controllers/admin</em>), copy initFormAttributes from there (about line 3628 in Prestashop 1.5.6), and paste the whole function inside the new override.</p>
<p>Then, locate the following:</p>
<pre class="brush: php; title: ; notranslate">
if ($this-&gt;product_exists_in_shop)
			{
				if ($product-&gt;is_virtual)
				{
					$data-&gt;assign('product', $product);
					$this-&gt;displayWarning($this-&gt;l('A virtual product cannot have combinations.'));
				}
				else
				{
					$attribute_js = array();
					$attributes = Attribute::getAttributes($this-&gt;context-&gt;language-&gt;id, true);
					foreach ($attributes as $k =&gt; $attribute)
						$attribute_js[$attribute['id_attribute_group']][$attribute['id_attribute']] = $attribute['name'];
					$currency = $this-&gt;context-&gt;currency;
					$data-&gt;assign('attributeJs', $attribute_js);
					$data-&gt;assign('attributes_groups', AttributeGroup::getAttributesGroups($this-&gt;context-&gt;language-&gt;id));

					$data-&gt;assign('currency', $currency);

					$images = Image::getImages($this-&gt;context-&gt;language-&gt;id, $product-&gt;id);

					$data-&gt;assign('tax_exclude_option', Tax::excludeTaxeOption());
					$data-&gt;assign('ps_weight_unit', Configuration::get('PS_WEIGHT_UNIT'));

					$data-&gt;assign('ps_use_ecotax', Configuration::get('PS_USE_ECOTAX'));
					$data-&gt;assign('field_value_unity', $this-&gt;getFieldValue($product, 'unity'));

					$data-&gt;assign('reasons', $reasons = StockMvtReason::getStockMvtReasons($this-&gt;context-&gt;language-&gt;id));
					$data-&gt;assign('ps_stock_mvt_reason_default', $ps_stock_mvt_reason_default = Configuration::get('PS_STOCK_MVT_REASON_DEFAULT'));
					$data-&gt;assign('minimal_quantity', $this-&gt;getFieldValue($product, 'minimal_quantity') ? $this-&gt;getFieldValue($product, 'minimal_quantity') : 1);
					$data-&gt;assign('available_date', ($this-&gt;getFieldValue($product, 'available_date') != 0) ? stripslashes(htmlentities($this-&gt;getFieldValue($product, 'available_date'), $this-&gt;context-&gt;language-&gt;id)) : '0000-00-00');

					$i = 0;
					$data-&gt;assign('imageType', ImageType::getByNameNType('small_default', 'products'));
					$data-&gt;assign('imageWidth', (isset($image_type['width']) ? (int)($image_type['width']) : 64) + 25);
					foreach ($images as $k =&gt; $image)
					{
						$images[$k]['obj'] = new Image($image['id_image']);
						++$i;
					}
					$data-&gt;assign('images', $images);

					$data-&gt;assign($this-&gt;tpl_form_vars);
					$data-&gt;assign(array(
						'list' =&gt; $this-&gt;renderListAttributes($product, $currency),
						'product' =&gt; $product,
						'id_category' =&gt; $product-&gt;getDefaultCategory(),
						'token_generator' =&gt; Tools::getAdminTokenLite('AdminAttributeGenerator'),
						'combination_exists' =&gt; (Shop::isFeatureActive() &amp;&amp; (Shop::getContextShopGroup()-&gt;share_stock) &amp;&amp; count(AttributeGroup::getAttributesGroups($this-&gt;context-&gt;language-&gt;id)) &gt; 0 &amp;&amp; $product-&gt;hasAttributes())
					));
				}
			}
			else
				$this-&gt;displayWarning($this-&gt;l('You must save the product in this shop before adding combinations.'));	
</pre>
<p>Can you see where it reads <strong>if ($product->is_virtual)</strong>? Get rid of this if/else, so it looks like this:</p>
<pre class="brush: php; title: ; notranslate">
			if ($this-&gt;product_exists_in_shop)
			{
				// removed virtual product restriction
				$attribute_js = array();
				$attributes = Attribute::getAttributes($this-&gt;context-&gt;language-&gt;id, true);
				foreach ($attributes as $k =&gt; $attribute)
					$attribute_js[$attribute['id_attribute_group']][$attribute['id_attribute']] = $attribute['name'];
				$currency = $this-&gt;context-&gt;currency;
				$data-&gt;assign('attributeJs', $attribute_js);
				$data-&gt;assign('attributes_groups', AttributeGroup::getAttributesGroups($this-&gt;context-&gt;language-&gt;id));

				$data-&gt;assign('currency', $currency);

				$images = Image::getImages($this-&gt;context-&gt;language-&gt;id, $product-&gt;id);

				$data-&gt;assign('tax_exclude_option', Tax::excludeTaxeOption());
				$data-&gt;assign('ps_weight_unit', Configuration::get('PS_WEIGHT_UNIT'));

				$data-&gt;assign('ps_use_ecotax', Configuration::get('PS_USE_ECOTAX'));
				$data-&gt;assign('field_value_unity', $this-&gt;getFieldValue($product, 'unity'));

				$data-&gt;assign('reasons', $reasons = StockMvtReason::getStockMvtReasons($this-&gt;context-&gt;language-&gt;id));
				$data-&gt;assign('ps_stock_mvt_reason_default', $ps_stock_mvt_reason_default = Configuration::get('PS_STOCK_MVT_REASON_DEFAULT'));
				$data-&gt;assign('minimal_quantity', $this-&gt;getFieldValue($product, 'minimal_quantity') ? $this-&gt;getFieldValue($product, 'minimal_quantity') : 1);
				$data-&gt;assign('available_date', ($this-&gt;getFieldValue($product, 'available_date') != 0) ? stripslashes(htmlentities($this-&gt;getFieldValue($product, 'available_date'), $this-&gt;context-&gt;language-&gt;id)) : '0000-00-00');

				$i = 0;
				$type = ImageType::getByNameNType('%', 'products', 'height');
				if (isset($type['name']))
					$data-&gt;assign('imageType', $type['name']);
				else
					$data-&gt;assign('imageType', 'small_default');
				$data-&gt;assign('imageWidth', (isset($image_type['width']) ? (int)($image_type['width']) : 64) + 25);
				foreach ($images as $k =&gt; $image)
				{
					$images[$k]['obj'] = new Image($image['id_image']);
					++$i;
				}
				$data-&gt;assign('images', $images);

				$data-&gt;assign($this-&gt;tpl_form_vars);
				$data-&gt;assign(array(
					'list' =&gt; $this-&gt;renderListAttributes($product, $currency),
					'product' =&gt; $product,
					'id_category' =&gt; $product-&gt;getDefaultCategory(),
					'token_generator' =&gt; Tools::getAdminTokenLite('AdminAttributeGenerator'),
					'combination_exists' =&gt; (Shop::isFeatureActive() &amp;&amp; (Shop::getContextShopGroup()-&gt;share_stock) &amp;&amp; count(AttributeGroup::getAttributesGroups($this-&gt;context-&gt;language-&gt;id)) &gt; 0 &amp;&amp; $product-&gt;hasAttributes())
				));
				
			}
			else
				$this-&gt;displayWarning($this-&gt;l('You must save the product in this shop before adding combinations.'));	
</pre>
<p>Save the file, go to <em>cache/</em> and delete <strong>class_index.php</strong>. Then access the product&#8217;s back office again, load a virtual product and click on the combinations tab. Nothing! At least, it&#8217;s better than the error message. We need to tackle another step.</p>
<div class="separator"></div>
<h2>Editing the attribute combinations template</h2>
<p>Inside the same folder where we placed our override, create the following structure: <em>templates/products/</em>. Then, reach the admin folder <em>themes/default/template/controllers/products</em>. Copy <strong>combinations.tpl</strong> from here into the other folder we just created.</p>
<p>At this point, open the newly cloned file, and right at the beginning we can read:</p>
<pre class="brush: php; html-script: true; title: ; notranslate">
{if isset($product-&gt;id) &amp;&amp; !$product-&gt;is_virtual}
</pre>
<p>Just get rid of <strong> &#038;&#038; !$product->is_virtual</strong>. Save and access the combinations tab again!</p>
<div class="separator"></div>
<h2>Finishing touches</h2>
<p>You can create combinations for a virtual product right away. However, if you try to convert an existing one into virtual, you will get the very same annoying message you got before. We need to hardcode a small fix in another couple of files, first: <strong>admin-products.js</strong>, which  can be found inside the <em>js</em> folder. Open it up and locate the following:</p>
<pre class="brush: jscript; title: ; notranslate">
				if (has_combinations)
				{
					$('#simple_product').attr('checked', true);
					$('#warn_virtual_combinations').show();
				}
				else
				{
					$('li.tab-row a[id*=&quot;VirtualProduct&quot;]').show().click();
					$('#is_virtual').val(1);

					tabs_manager.onLoad('VirtualProduct', function(){
						$('#is_virtual_good').attr('checked', true);
						$('#virtual_good').show();
					});

					tabs_manager.onLoad('Quantities', function(){
						$('.stockForVirtualProduct').hide();
					});

					$('li.tab-row a[id*=&quot;Shipping&quot;]').hide();

					tabs_manager.onLoad('Informations', function(){
						$('#condition').attr('disabled', true);
						$('#condition option[value=refurbished]').removeAttr('selected');
						$('#condition option[value=used]').removeAttr('selected');
					});
				}
</pre>
<p>Change it to</p>
<pre class="brush: jscript; title: ; notranslate">

					$('li.tab-row a[id*=&quot;VirtualProduct&quot;]').show().click();
					$('#is_virtual').val(1);

					tabs_manager.onLoad('VirtualProduct', function(){
						$('#is_virtual_good').attr('checked', true);
						$('#virtual_good').show();
					});

					tabs_manager.onLoad('Quantities', function(){
						$('.stockForVirtualProduct').hide();
					});

					$('li.tab-row a[id*=&quot;Shipping&quot;]').hide();

					tabs_manager.onLoad('Informations', function(){
						$('#condition').attr('disabled', true);
						$('#condition option[value=refurbished]').removeAttr('selected');
						$('#condition option[value=used]').removeAttr('selected');
					});
				
</pre>
<p>Then, once more, reach the <em>themes/default/template/controllers/products</em> folder. This time, copy <strong>virtualproduct.tpl</strong> to the same folder where we added combinations.tpl. Open it up and inspect the following snippet:</p>
<pre class="brush: php; html-script: true; title: ; notranslate">
			{* Don't display file form if the product has combinations *}
			{if empty($product-&gt;cache_default_attribute)}
				{if $product-&gt;productDownload-&gt;id}
					&lt;input type=&quot;hidden&quot; id=&quot;virtual_product_id&quot; name=&quot;virtual_product_id&quot; value=&quot;{$product-&gt;productDownload-&gt;id}&quot; /&gt;
				{/if}
				&lt;table cellpadding=&quot;5&quot; style=&quot;float: left; margin-left: 10px;&quot;&gt;
					&lt;tr id=&quot;upload_input&quot; {if $is_file}style=&quot;display:none&quot;{/if}&gt;
						&lt;td class=&quot;col-left&quot;&gt;
							&lt;label id=&quot;virtual_product_file_label&quot; for=&quot;virtual_product_file&quot; class=&quot;t&quot;&gt;{l s='Upload a file'}&lt;/label&gt;
						&lt;/td&gt;
						&lt;td class=&quot;col-right&quot;&gt;
							&lt;input type=&quot;file&quot; id=&quot;virtual_product_file&quot; name=&quot;virtual_product_file&quot; onchange=&quot;uploadFile();&quot; maxlength=&quot;{$upload_max_filesize}&quot; /&gt;
							&lt;p class=&quot;preference_description&quot;&gt;{l s='Your server\'s maximum file-upload size is'}:&amp;nbsp;{$upload_max_filesize} {l s='MB'}&lt;/p&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
					&lt;tr id=&quot;upload-error&quot; style=&quot;display:none&quot;&gt;
						&lt;td colspan=2&gt;&lt;/td&gt;
					&lt;/tr&gt;
					&lt;tr id=&quot;upload-confirmation&quot; style=&quot;display:none&quot;&gt;
						&lt;td colspan=2&gt;
							{if $up_filename}
								&lt;input type=&quot;hidden&quot; id=&quot;virtual_product_filename&quot; name=&quot;virtual_product_filename&quot; value=&quot;{$up_filename}&quot; /&gt;
							{/if}
							&lt;div class=&quot;conf&quot;&gt;
							&lt;script&gt;
								delete_this_file = '{l s='Delete this file'}';
							&lt;/script&gt;
								&lt;a class=&quot;delete_virtual_product&quot; id=&quot;delete_downloadable_product&quot; href=&quot;{$currentIndex}&amp;deleteVirtualProduct=true&amp;token={$token}&amp;id_product={$product-&gt;id}&quot; class=&quot;red&quot;&gt;
									&lt;img src=&quot;../img/admin/delete.gif&quot; alt=&quot;{l s='Delete this file'}&quot;/&gt;
								&lt;/a&gt;
							&lt;/div&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
					{if $is_file}
						&lt;tr&gt;
							&lt;td class=&quot;col-left&quot;&gt;
								&lt;input type=&quot;hidden&quot; id=&quot;virtual_product_filename&quot; name=&quot;virtual_product_filename&quot; value=&quot;{$product-&gt;productDownload-&gt;filename}&quot; /&gt;
								&lt;label class=&quot;t&quot;&gt;{l s='Link to the file:'}&lt;/label&gt;
							&lt;/td&gt;
							 &lt;td class=&quot;col-right&quot;&gt;
								{$product-&gt;productDownload-&gt;getHtmlLink(false, true)}
								&lt;a href=&quot;{$currentIndex}&amp;deleteVirtualProduct=true&amp;token={$token}&amp;id_product={$product-&gt;id}&quot; class=&quot;red delete_virtual_product&quot;&gt;
									&lt;img src=&quot;../img/admin/delete.gif&quot; alt=&quot;{l s='Delete this file'}&quot;/&gt;
								&lt;/a&gt;
							&lt;/td&gt;
						&lt;/tr&gt;
					{/if}
					&lt;tr&gt;
						&lt;td class=&quot;col-left&quot;&gt;
							&lt;label for=&quot;virtual_product_name&quot; class=&quot;t&quot;&gt;{l s='Filename'}&lt;/label&gt;
						&lt;/td&gt;
						&lt;td class=&quot;col-right&quot;&gt;
							&lt;input type=&quot;text&quot; id=&quot;virtual_product_name&quot; name=&quot;virtual_product_name&quot; style=&quot;width:200px&quot; value=&quot;{$product-&gt;productDownload-&gt;display_filename|escape:'htmlall':'UTF-8'}&quot; /&gt;
							&lt;p class=&quot;preference_description&quot; name=&quot;help_box&quot;&gt;{l s='The full filename with its extension (e.g. Book.pdf)'}&lt;/p&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
					&lt;tr&gt;
						&lt;td class=&quot;col-left&quot;&gt;
							&lt;label for=&quot;virtual_product_nb_downloable&quot; class=&quot;t&quot;&gt;{l s='Number of allowed downloads'}&lt;/label&gt;
						&lt;/td&gt;
						&lt;td class=&quot;col-right&quot;&gt;
							&lt;input type=&quot;text&quot; id=&quot;virtual_product_nb_downloable&quot; name=&quot;virtual_product_nb_downloable&quot; value=&quot;{$product-&gt;productDownload-&gt;nb_downloadable|htmlentities}&quot; class=&quot;&quot; size=&quot;6&quot; /&gt;
							&lt;p class=&quot;preference_description&quot;&gt;{l s='Number of downloads allowed per customer. (Set to 0 for unlimited downloads)'}&lt;/p&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
					&lt;tr&gt;
						&lt;td class=&quot;col-left&quot;&gt;
							&lt;label for=&quot;virtual_product_expiration_date&quot; class=&quot;t&quot;&gt;{l s='Expiration date'}&lt;/label&gt;
						&lt;/td&gt;
						&lt;td class=&quot;col-right&quot;&gt;
							&lt;input class=&quot;datepicker&quot; type=&quot;text&quot; id=&quot;virtual_product_expiration_date&quot; name=&quot;virtual_product_expiration_date&quot; value=&quot;{$product-&gt;productDownload-&gt;date_expiration}&quot; size=&quot;11&quot; maxlength=&quot;10&quot; autocomplete=&quot;off&quot; /&gt; {l s='Format: YYYY-MM-DD'}
							&lt;p class=&quot;preference_description&quot;&gt;{l s='If set, the file will not be downloadable after this date. Leave blank if you do not wish to attach an expiration date.'}&lt;/p&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
						&lt;td class=&quot;col-left&quot;&gt;
							&lt;label for=&quot;virtual_product_nb_days&quot; class=&quot;t&quot;&gt;{l s='Number of days'}&lt;/label&gt;
						&lt;/td&gt;
						&lt;td class=&quot;col-right&quot;&gt;
							&lt;input type=&quot;text&quot; id=&quot;virtual_product_nb_days&quot; name=&quot;virtual_product_nb_days&quot; value=&quot;{$product-&gt;productDownload-&gt;nb_days_accessible|htmlentities}&quot; class=&quot;&quot; size=&quot;4&quot; /&gt;&lt;sup&gt; *&lt;/sup&gt;
							&lt;p class=&quot;preference_description&quot;&gt;{l s='Number of days this file can be accessed by customers'} - &lt;em&gt;({l s='Set to zero for unlimited access.'})&lt;/em&gt;&lt;/p&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
					{* Feature not implemented *}
					{*&lt;tr&gt;*}
						{*&lt;td class=&quot;col-left&quot;&gt;*}
							{*&lt;label for=&quot;virtual_product_is_shareable&quot; class=&quot;t&quot;&gt;{l s='is shareable'}&lt;/label&gt;*}
						{*&lt;/td&gt;*}
						{*&lt;td class=&quot;col-right&quot;&gt;*}
							{*&lt;input type=&quot;checkbox&quot; id=&quot;virtual_product_is_shareable&quot; name=&quot;virtual_product_is_shareable&quot; value=&quot;1&quot; {if $product-&gt;productDownload-&gt;is_shareable}checked=&quot;checked&quot;{/if} /&gt;*}
							{*&lt;span class=&quot;hint&quot; name=&quot;help_box&quot; style=&quot;display:none&quot;&gt;{l s='Please specify if the file can be shared.'}&lt;/span&gt;*}
						{*&lt;/td&gt;*}
					{*&lt;/tr&gt;*}
				{else}
					&lt;div class=&quot;hint clear&quot; style=&quot;display: block;width: 70%;&quot;&gt;{l s='You cannot edit your file here because you used combinations. Please edit this file in the Combinations tab.'}&lt;/div&gt;
					&lt;br /&gt;
					{if isset($error_product_download)}{$error_product_download}{/if}
				{/if}
</pre>
<p>Change it to:</p>
<pre class="brush: php; html-script: true; title: ; notranslate">

			{if $product-&gt;productDownload-&gt;id}
				&lt;input type=&quot;hidden&quot; id=&quot;virtual_product_id&quot; name=&quot;virtual_product_id&quot; value=&quot;{$product-&gt;productDownload-&gt;id}&quot; /&gt;
			{/if}
			&lt;table cellpadding=&quot;5&quot; style=&quot;float: left; margin-left: 10px;&quot;&gt;
				&lt;tr id=&quot;upload_input&quot; {if $is_file}style=&quot;display:none&quot;{/if}&gt;
					&lt;td class=&quot;col-left&quot;&gt;
						&lt;label id=&quot;virtual_product_file_label&quot; for=&quot;virtual_product_file&quot; class=&quot;t&quot;&gt;{l s='Upload a file'}&lt;/label&gt;
					&lt;/td&gt;
					&lt;td class=&quot;col-right&quot;&gt;
						&lt;input type=&quot;file&quot; id=&quot;virtual_product_file&quot; name=&quot;virtual_product_file&quot; onchange=&quot;uploadFile();&quot; maxlength=&quot;{$upload_max_filesize}&quot; /&gt;
						&lt;p class=&quot;preference_description&quot;&gt;{l s='Your server\'s maximum file-upload size is'}:&amp;nbsp;{$upload_max_filesize} {l s='MB'}&lt;/p&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
				&lt;tr id=&quot;upload-error&quot; style=&quot;display:none&quot;&gt;
					&lt;td colspan=2&gt;&lt;/td&gt;
				&lt;/tr&gt;
				&lt;tr id=&quot;upload-confirmation&quot; style=&quot;display:none&quot;&gt;
					&lt;td colspan=2&gt;
						{if $up_filename}
							&lt;input type=&quot;hidden&quot; id=&quot;virtual_product_filename&quot; name=&quot;virtual_product_filename&quot; value=&quot;{$up_filename}&quot; /&gt;
						{/if}
						&lt;div class=&quot;conf&quot;&gt;
						&lt;script&gt;
							delete_this_file = '{l s='Delete this file'}';
						&lt;/script&gt;
							&lt;a class=&quot;delete_virtual_product&quot; id=&quot;delete_downloadable_product&quot; href=&quot;{$currentIndex}&amp;deleteVirtualProduct=true&amp;token={$token}&amp;id_product={$product-&gt;id}&quot; class=&quot;red&quot;&gt;
								&lt;img src=&quot;../img/admin/delete.gif&quot; alt=&quot;{l s='Delete this file'}&quot;/&gt;
							&lt;/a&gt;
						&lt;/div&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
				{if $is_file}
					&lt;tr&gt;
						&lt;td class=&quot;col-left&quot;&gt;
							&lt;input type=&quot;hidden&quot; id=&quot;virtual_product_filename&quot; name=&quot;virtual_product_filename&quot; value=&quot;{$product-&gt;productDownload-&gt;filename}&quot; /&gt;
							&lt;label class=&quot;t&quot;&gt;{l s='Link to the file:'}&lt;/label&gt;
						&lt;/td&gt;
						 &lt;td class=&quot;col-right&quot;&gt;
							{$product-&gt;productDownload-&gt;getHtmlLink(false, true)}
							&lt;a href=&quot;{$currentIndex}&amp;deleteVirtualProduct=true&amp;token={$token}&amp;id_product={$product-&gt;id}&quot; class=&quot;red delete_virtual_product&quot;&gt;
								&lt;img src=&quot;../img/admin/delete.gif&quot; alt=&quot;{l s='Delete this file'}&quot;/&gt;
							&lt;/a&gt;
						&lt;/td&gt;
					&lt;/tr&gt;
				{/if}
				&lt;tr&gt;
					&lt;td class=&quot;col-left&quot;&gt;
						&lt;label for=&quot;virtual_product_name&quot; class=&quot;t&quot;&gt;{l s='Filename'}&lt;/label&gt;
					&lt;/td&gt;
					&lt;td class=&quot;col-right&quot;&gt;
						&lt;input type=&quot;text&quot; id=&quot;virtual_product_name&quot; name=&quot;virtual_product_name&quot; style=&quot;width:200px&quot; value=&quot;{$product-&gt;productDownload-&gt;display_filename|escape:'htmlall':'UTF-8'}&quot; /&gt;
						&lt;p class=&quot;preference_description&quot; name=&quot;help_box&quot;&gt;{l s='The full filename with its extension (e.g. Book.pdf)'}&lt;/p&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
				&lt;tr&gt;
					&lt;td class=&quot;col-left&quot;&gt;
						&lt;label for=&quot;virtual_product_nb_downloable&quot; class=&quot;t&quot;&gt;{l s='Number of allowed downloads'}&lt;/label&gt;
					&lt;/td&gt;
					&lt;td class=&quot;col-right&quot;&gt;
						&lt;input type=&quot;text&quot; id=&quot;virtual_product_nb_downloable&quot; name=&quot;virtual_product_nb_downloable&quot; value=&quot;{$product-&gt;productDownload-&gt;nb_downloadable|htmlentities}&quot; class=&quot;&quot; size=&quot;6&quot; /&gt;
						&lt;p class=&quot;preference_description&quot;&gt;{l s='Number of downloads allowed per customer. (Set to 0 for unlimited downloads)'}&lt;/p&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
				&lt;tr&gt;
					&lt;td class=&quot;col-left&quot;&gt;
						&lt;label for=&quot;virtual_product_expiration_date&quot; class=&quot;t&quot;&gt;{l s='Expiration date'}&lt;/label&gt;
					&lt;/td&gt;
					&lt;td class=&quot;col-right&quot;&gt;
						&lt;input class=&quot;datepicker&quot; type=&quot;text&quot; id=&quot;virtual_product_expiration_date&quot; name=&quot;virtual_product_expiration_date&quot; value=&quot;{$product-&gt;productDownload-&gt;date_expiration}&quot; size=&quot;11&quot; maxlength=&quot;10&quot; autocomplete=&quot;off&quot; /&gt; {l s='Format: YYYY-MM-DD'}
						&lt;p class=&quot;preference_description&quot;&gt;{l s='If set, the file will not be downloadable after this date. Leave blank if you do not wish to attach an expiration date.'}&lt;/p&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
					&lt;td class=&quot;col-left&quot;&gt;
						&lt;label for=&quot;virtual_product_nb_days&quot; class=&quot;t&quot;&gt;{l s='Number of days'}&lt;/label&gt;
					&lt;/td&gt;
					&lt;td class=&quot;col-right&quot;&gt;
						&lt;input type=&quot;text&quot; id=&quot;virtual_product_nb_days&quot; name=&quot;virtual_product_nb_days&quot; value=&quot;{$product-&gt;productDownload-&gt;nb_days_accessible|htmlentities}&quot; class=&quot;&quot; size=&quot;4&quot; /&gt;&lt;sup&gt; *&lt;/sup&gt;
						&lt;p class=&quot;preference_description&quot;&gt;{l s='Number of days this file can be accessed by customers'} - &lt;em&gt;({l s='Set to zero for unlimited access.'})&lt;/em&gt;&lt;/p&gt;
					&lt;/td&gt;
				&lt;/tr&gt;
				{* Feature not implemented *}
				{*&lt;tr&gt;*}
					{*&lt;td class=&quot;col-left&quot;&gt;*}
						{*&lt;label for=&quot;virtual_product_is_shareable&quot; class=&quot;t&quot;&gt;{l s='is shareable'}&lt;/label&gt;*}
					{*&lt;/td&gt;*}
					{*&lt;td class=&quot;col-right&quot;&gt;*}
						{*&lt;input type=&quot;checkbox&quot; id=&quot;virtual_product_is_shareable&quot; name=&quot;virtual_product_is_shareable&quot; value=&quot;1&quot; {if $product-&gt;productDownload-&gt;is_shareable}checked=&quot;checked&quot;{/if} /&gt;*}
						{*&lt;span class=&quot;hint&quot; name=&quot;help_box&quot; style=&quot;display:none&quot;&gt;{l s='Please specify if the file can be shared.'}&lt;/span&gt;*}
					{*&lt;/td&gt;*}
				{*&lt;/tr&gt;*}

</pre>
<p><strong>Explanation:</strong> we basically got rid of the if/else statement which was triggering the message, instead of displaying the virtual product form.</p>
<p>Save, and we are done!</p>
<h2>A note about prestashop 1.6</h2>
<p>Iclavijos, user of the Prestashop forums, found out an additional step is needed in order to make virtual products work with combinations in Prestashop 1.6, here follows his advice</p>
<p>You need to override form.tpl (controllers/admin/templates/products/helpers/form) and there, comment out line 116. Basically, this is the original code:</p>
<pre class="brush: php; title: ; notranslate">
else if (product_type == product_type_virtual)

                {

                    $('a[id*=&quot;Pack&quot;]').hide();

                    $('a[id*=&quot;Shipping&quot;]').hide();

                    $('a[id*=&quot;Combinations&quot;]').hide();

                }[
</pre>
<p>and this should be the final one:</p>
<pre class="brush: php; title: ; notranslate">
else if (product_type == product_type_virtual)

                {

                    $('a[id*=&quot;Pack&quot;]').hide();

                    $('a[id*=&quot;Shipping&quot;]').hide();

                    //$('a[id*=&quot;Combinations&quot;]').hide();

                }
</pre>
<p>Thanks for your contribution!</p>
<div class="separator"></div>
<h2>Final note</h2>
<p>Although we used overrides to ensure changes are preserved after an upgrade, remember to re-apply the admin-products.js fix, as we had to hardcode that one!.</p>
<p>The post <a rel="nofollow" href="https://nemops.com/prestashop-virtual-products-combinations/">Enable Combinations for Virtual Products in Prestashop 1.6/1.5</a> appeared first on <a rel="nofollow" href="https://nemops.com">NemoPS</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://nemops.com/prestashop-virtual-products-combinations/feed/</wfw:commentRss>
		<slash:comments>24</slash:comments>
		</item>
	</channel>
</rss>
