The possibility to use uc_vat with uc_price_per_role is really very limited without the ability to enter prices per role inclusive of VAT.

uc_vat needs to add support, like it did for uc_multiprice #489198-16: Allow inputing of VAT inclusive prices, to enter prices per role inclusive of VAT.

Comments

fenstrat’s picture

Status: Active » Needs review
StatusFileSize
new4.52 KB
fenstrat’s picture

Forgot to mention that this depends on #724900: Use uc_price() instead of uc_currency_format()

That patch adds support for uc_price() and also alters the option prices form to include the unformatted original price as a hidden value. This simplifies this patches' form alteration here in uc_vat. Otherwise uc_vat would have to do additional queries to look up prices from uc_price_per_role tables.

fenstrat’s picture

StatusFileSize
new4.5 KB

Simple rerole of #1 to keep up with dev version.

longwave’s picture

Thanks for the patch. However, I'm currently reluctant to commit any support like this, as if I'm not careful we will have a huge number of form alter functions that all look very similar. I think instead uc_vat needs to provide either some kind of hook or FormAPI extension that makes it simpler for modules to say "this is a price that could be VAT altered" and somehow save copy and pasting an alter function and submit handler for each one. Ideally I'd like other modules to be able to support this simply by adding a line or two to their own form functions.

I will be testing some out some code to do this as soon as I can, but any ideas on how to approach this sort of thing are welcome!

fenstrat’s picture

Status: Needs review » Needs work

Perfectly understandable. I'd seen your previous comments about wanting to abstract this so other modules could hook into it. But at the time just needed a working solution.

A FAPI extension could be one way to go.
If a module declared its price like:

$form['element']['cost'] = array(
  '#type' => 'textfield',
  '#default_value' => uc_store_format_price_field_value($price),
  '#uc_vat_alterable_price' => 'cost',
);

Then it'd be up to uc_vat to look for these and alter accordingly. To get around module weighting issues uc_vat could do this by adding a form after_build callback. A submit hander would also need to be attached to remove the VAT on save if needed. But how best to know which forms to examine?

The other way could be a direct call to somthing like uc_vat_price() which returns the price inclusive or exclusive of VAT for use in module admin forms. However this wouldn't allow a submit handler to be attached.

The solution will also need to cover prices for display (i.e. non form inputs). E.g. in this patch #724900: Use uc_price() instead of uc_currency_format() I had to add a hidden unformatted form value so as the alterer here in uc_vat could easily recompute the non VAT price for display.

Just ideas of the top of my head for now. Will think on it more.

longwave’s picture

Thanks for your input on this! I was thinking along the same lines for a FAPI extension, basically. Perhaps the form should also set $form['#uc_vat_process'] = TRUE to so uc_vat can quickly check for alterations without having to iterate through all elements on all forms. For some prices we also need to know the node type and shippable flag so we can calculate the taxes correctly, this would also have to be stored in the FAPI array somewhere - for forms that cover multiple nodes we cannot rely on just $form['#node'] for this.

I don't like the uc_vat_price() idea as this moves more specific VAT support into each module, which I was trying to avoid, and as you say we still need to handle altering the prices again on form submission. It would be better if modules can implement this themselves, or if their maintainer has no interest in VAT support, a small glue module can be written that provides uc_vat support on behalf of the other module.

My other idea was to implement a hook where modules declare the forms they use that can be altered, but this seems to be unnecessary if we can use FAPI to achieve the same thing in a simpler way.

For non-form inputs, there's so many ways a price can be output that I don't think we can cover them all. However, these prices should be generated by uc_price(), so perhaps we can add an extra flag to the context that lets uc_vat's price alterer decide whether or not to alter prices in more specific cases? A 'location' was provided in context earlier in uc_price's development, but unfortunately this was dropped as we could not see any use cases for it at the time! But again this means the calling module has to declare this, and other modules cannot override this easily; maybe a hook would work for this, that declares URLs/path where the price alterer should/should not be run depending on the price editing configuration options? This would likely also need extra checks on $context to ensure we do not inadvertantly override modifying cart prices on pages where these altered prices are displayed...

fenstrat’s picture

Title: Add support for VAT inclusive prices in uc_price_per_role » Add support for other modules to enter prices inclusive of VAT

No worries, this is an interesting problem.

Certainly need to scrap the uc_vat_price() idea.

The FAPI extension sounds promising, $form['#uc_vat_process'] = TRUE would simplify checking which forms need alteration in uc_vat_alter_input_prices(). Then on an element level:

$form['element']['cost'] = array(
  '#type' => 'textfield',
  '#default_value' => uc_store_format_price_field_value($price),
  '#uc_vat_alterable_price' => array(
    '#price_type' => 'cost',
    '#subject' => $product,
  ),
);

From #subject we could then extract node type and shippable.

The only way I can see this working consistently is to adjust the module weight of uc_vat so it runs after all other form_alters.
Or from within uc_vat_form_alter() to add an #after_build callback of uc_vat_alter_input_prices() to all forms. This would ensure uc_vat_alter_input_prices() ran after uc_price_per_role has had a chance to alter the attribute options form.
uc_vat_alter_input_prices() is where the check for #uc_vat_process is run and which then applies any required altering plus added submit handler.
I've not come across this approach before, it should work. If it doesn't the hook approach you mentioned may need to be looked at.

Non-form inputs will be tricky. Pitty about 'location' being removed from uc_price()! The $options param doesn't seem to 'clamp' to the list of allowed values. Could we add onto this? Probably not.
A hook could work and as you suggested would have to do so by declaring url/paths where the alter needs to run.

I should have some more time at the end of the week to look at this.

nicolasb_cdip’s picture

I don't know if this can help but I've been working on this kind of issue to make modules like uc_coupon, uc_discount_alt and uc_gift_certificate handle vat.
So here is what I've done :
Added a function called get_uc_price in uc_vat.module which looks like :

/**
+ * Returns the price for a product including Tax,
+ *
+ * @param object $item
+ */
function uc_vat_get_uc_price($item,$qty=0) {
	if ($qty==0) $qty=$item->qty;
  $price_info = array(
    'price' => $item->price,
    'qty' => $qty,
  );
  $context = array(
    'type' => 'product',
    'revision' => 'altered',
    'subject' => array(
      'product' => $item,
      'node' => node_load($item->nid),
    ),
  );
  return uc_price($price_info, $context);
}

This function should be called by the modules whenever they want a price to be displayed or calculated inclusive of VAT.
So, for instance, in the uc_gift_certificate, the amount of a gift certificate you purchase is exclusive of VAT but the amount you pay is inclusive of VAT. So to fix this issue, using the above function, a patch would looks like :

         $new_gc = new stdClass();
         $new_gc->name = $title;
-        $new_gc->value = $product->qty * $product->price;
+		$new_gc->value = $product->qty * $product->price;
+		foreach (module_implements('get_uc_price') as $module) {
+			$fonction=$module."_get_uc_price";
+			$new_gc->value = $fonction($product);
+		}
         $new_gc->purchaser_id = $order->uid;
         $new_gc->order_id = $order->order_id;

The way the code is written is specific to the module use and should insure the price is not added twice or some other mistake like this.

Does this look like the solution you were thinking about ?

fenstrat’s picture

Thanks for the ideas nicolasb_cdip.

It's certainly one way to approach it, however I'm not sure that the use of module_implements() is correct as that function is meant to allow more than one module to provide a response to a hook. Whereas what we're after is uc_vat to always handle the price.

The key is to support sites that don't have uc_vat installed, so uc_gift_certificate and others can run independent of uc_vat, but also with uc_vat enabled. I'm guessing this is what you were trying to achieve with module_implements, just not sure it's the right way to go about it.

.
As with all good "side projects" I haven't had a chance to look into this again. One issue that hasn't been addressed is the outputting of notes to let the user know that altered fields are entered in VAT inclusive. From the above patch:

+  $form['vat_note'] = array(
+    '#value' => '<p>'. t('Note: All option prices include !tax.', array('!tax' => variable_get('uc_vat_name', 'VAT'))) .'</p>',
+  );

Would it be acceptable if this note was always be added on at the base $form level if we go with the FAPI extension approach?

longwave’s picture

@nicolasb_cdip: I'm not sure what you're trying to do with module_implements() there but it doesn't look like correct usage, if multiple modules implement your hook what do you expect to happen? It looks like only the last module to be called will have any effect. Also your hook_get_uc_price() function defeats the point of using uc_price() in the first place, it assumes that the price being altered is a product, when for the purposes of this particular issue, we cannot assume that - I intend to be able to alter shipping prices here, for example, which are not products or even nodes.

@fenstrat: I think that adding vat_note either to the top or bottom of forms affected by this code would be acceptable, maybe allowing it to be switched on and off via a site-wide configuration variable, and also through a further FAPI extension for specific forms where this technique does not work well?

fenstrat’s picture

StatusFileSize
new4.49 KB

This is just an update to the patch in #3 to apply to uc_vat-6.x-1.2

I've had no time to look at the generic solution discussed in #5 and beyond.

fenstrat’s picture

StatusFileSize
new4.58 KB

An update to the patch in #11 to fix the "Leave any box blank to use the default price for the option." behaviour.

The change included in this patch from #11 only alters an attribute price if it is non zero. This fixes an issue where blank attribute fields (which should be left blank) actually had their VAT calculated and therefore a zero value ("0.00000") was inserted in to the {uc_price_per_role_option_prices} table. If you've been running the patch from #11 you'll have zero value entires in that table where there shouldn't be. This can be fixed with simple query like "DELETE FROM {uc_price_per_role_option_prices} WHERE price = '0.00000'". This patch here fixes that issue.

ps. This patch provides a fully working solution for integrating uc_vat with uc_price_per_role, however this is unlikely to be committed to uc_vat because longwave (uc_vat's maintainer) would like a more generic solution so that other modules may define their integration with uc_vat, rather than uc_vat defining the integration for other modules (as this patch does).

jansete’s picture

Hi everybody, I had the same problem, but I could fix it,

You don't need put any patch
You must to change these line in case 'load' in the function uc_price_per_role_nodeapi in uc_price_per_role.module file

case 'load':
        $result = db_query("SELECT rid, price FROM {uc_price_per_role_prices} WHERE vid = %d", $node->vid);
        $prices = array();
        while ($row = db_fetch_object($result)) {
          $prices[$row->rid] = $row->price;
        }

        $original_price = $node->sell_price;
        $price = uc_price_per_role_find_price($prices);
        if ($price !== FALSE) {
			$taxes = uc_vat_load_taxes(); //THIS
			foreach (array_reverse($taxes) as $tax) { //& THIS
					$node->sell_price = $price/(1 + $tax->rate); //& THIS
			}//& THIS
        }
carlodimartino’s picture

Just thought I'd thank you jansete. I just found this post and it fixed my problem!

jvieille’s picture

Issue summary: View changes

Forget about #13: it does adjust the price on display only, applies all configured taxes regardless the product and is incompatble with the proposed patch.