News:

Support the VirtueMart project and become a member

Main Menu

Developers: Please confirm if i done it right ?

Started by veeco, December 28, 2012, 10:09:26 AM

Previous topic - Next topic

veeco

Hi Devs,
this week as been a 'breakthrough' for me as i'm now able to create payment and shipment plugin. I'm now still target to be able to create field plugin and calculation. In this thread i would like to ask you opinion and confirmation. I'm able to create UI in the cart interface (as attached).

I don't know if i'm doing it right or wrong as all done through a self learning without any dedicated mentor/guide.

Now my question is, i do all the UI including adding form, etc in Function renderPluginName().
It's bit complicated since all method param,cart, etc are done through process of $this variable while in other function we have $method variable already.

So, am i doing it right ?

Thanks


[attachment cleanup by admin]
Work On Doxa Web Developer Indonesia - http://doxadigital.com |

veeco

Work On Doxa Web Developer Indonesia - http://doxadigital.com |

veeco

::bump::
really wish some virtuemart devs (or maybe core team) can solve this issue.... it just simply matter of saying yes or no...
i already tried to contribute here...
Work On Doxa Web Developer Indonesia - http://doxadigital.com |

stAn99

hello, i am a newbie dev here, but i would strongly recommend you to check other plugins to learn how to write your own. for example fedex, usps and ups are also multi shipment methods which generate more than one method per plugin if this is what you are trying to do.

To render your own html code within a shipment plugin you can use these functions:

// this function will render your html into FE (front end) shown as a checkout step or at the checkout, you should use name="virtuemart_shipmentmethod_id" for your radio input

plgVmDisplayListFEShipment (VirtueMartCart $cart, $selected = 0, &$htmlIn)
(attached below - the code below might not be fully functional)

if your plugin does not generate more than one option per your plugin and you do not need a special html, you can use the default's vmplugin function this way:

public function plgVmDisplayListFEShipment (VirtueMartCart $cart, $selected = 0, &$htmlIn) {

return $this->displayListFE ($cart, $selected, $htmlIn);
}


Example when having your own html code:


/**
* plgVmDisplayListFE
* This event is fired to display the pluginmethods in the cart (edit shipment/payment) for example
*
* @param object  $cart Cart object
* @param integer $selected ID of the method selected
* @return boolean True on success, false on failures, null when this plugin was not selected.
* On errors, JError::raiseWarning (or JError::raiseError) must be used to set a message.
*
* @author Valerie Isaksen
* @author Max Milbers
*/
public function plgVmDisplayListFEShipment (VirtueMartCart $cart, $selected = 0, &$htmlIn) {
if ($this->getPluginMethods($cart->vendorId) === 0) {
  return false;
}
if (!class_exists('vmJsApi'))
require_once(JPATH_ADMINISTRATOR.DS.'components'.DS.'com_virtuemart'.DS.'helpers'.DS.'config.php');
$arr = array();
foreach ($this->methods as $method)
{
    $html =  '<div style="display: none;"><input type="radio" name="virtuemart_shipmentmethod_id" id="pickup_shipping"   value="' . $method->virtuemart_shipmentmethod_id .'" ';
if (empty($selected))
{
$html .= '  ';
$selected = $method->virtuemart_shipmentmethod_id;
}


$html .= '/><input type="hidden" name="selected_method" id="free_or_pickup_selector" value="pickup" /></div>';
$cond = $this->free_checkConditions ($cart, $method, null);

// jumphere
$html .= '
  <div class="pf_shipping">
  <div class="pf_pickup">
  <div class="opc_heading"><button class="pickup_checkbox button_checkbox_ed" name="pickup_checkbox" onclick="javascript:return pf_checkbox(\'pickup\', \'orfree\');" id="pickup_checkbox"><div>&nbsp;</div></button><span class="opc_title">Pick Up</span></div>
<div class="opc_inside">
<div>
  <div class="field_wrapper">
    <div class="formLabel">
  <label for="pickup_date">Date:</label>
</div>
    <div class="formField">

  ';
  //$html .= JHTML::calendar($date='',$name="date",$id=NULL,$resetBt = TRUE, $yearRange='');
  $cal = vmJsApi::jDate('', 'my_date', 'pickup', true, '');
 
  //vmJsApi::jDate($_return['fields'][$_fld->name]['value'],  $_prefix.$_fld->name,$_prefix.$_fld->name . '_field',false,($currentYear-100).':'.$currentYear);
  //$cal = JHTML::calendar('', 'my_calendar', 'pickup_date_input', '%d-%m-%Y', '');
  $html .= $cal;
  $html .= '<div style="margin-left: 90%;position:absolute;left:0;top:5px;z-index:0;"><img src="/templates/beez_20/images/system/calendar.png" onclick="return jQuery( function($) { $(\'.datepickervm\').datepicker(\'show\'); } );" alt="Calendar" class="calendar" id="_img"></div>';
$html.= '</div>
</div>

<div class="field_wrapper">
<div class="formLabel">
  <label for="pickup_date">Time:</label>
</div>
    <div class="formField">
  <select name="pickup_time" >';
   $f1 = $method->pickup_start_time;
   $f2 = explode(':', $f1);
   $from = $f2[0];

   $f1 = $method->pickup_end_time;
   $f2 = explode(':', $f1);
   $to = $f2[0];
    $it = (int)$method->time_period;
   $c = 60 / $it;
   for ($i = $from; $i<=$to; $i++)
    {
  $html .= '<option value="'.$i.':00">'.$i.':00</option>';
  for ($q = 1; $q<$c; $q++)
  {
    $j = $q*$it;
    $html .= ' <option value="'.$i.':'.$j.'">'.$i.':'.$j.'</option>';
  }
}
   
  $html .= '
  </select>
</div>


  </div>


</div>
</div>
 
  </div>
  <div class="pf_free">
  <div class="opc_heading"><button ';
  if (!$cond) $html .= ' disabled="disabled" ';
  $html .= ' class="pickup_checkbox button_checkbox_uned" name="free_checkbox" onclick="javascript:return pf_checkbox(\'orfree\', \'pickup\');" id="free_checkbox"><div>&nbsp;</div></button><span class="opc_title">Free Delivery</span></div>
<div class="opc_inside">
<div>';
if ($cond)
{
$html .='<div class="field_wrapper">
    <div class="formLabel">
  <label for="free_date">Date:</label>
</div>
    <div class="formField">

  ';
  // jDate($date='',$name="date",$id=NULL,$resetBt = TRUE, $yearRange='')
  $cal = vmJsApi::jDate('', 'free_date', 'free_date', true, '');
 
  //vmJsApi::jDate($_return['fields'][$_fld->name]['value'],  $_prefix.$_fld->name,$_prefix.$_fld->name . '_field',false,($currentYear-100).':'.$currentYear);
  //$cal = JHTML::calendar('', 'my_calendar', 'pickup_date_input', '%d-%m-%Y', '');
  $html .= $cal;
$html.= '</div>
</div>

<div class="field_wrapper pf_field_wrapper">';



$html .= '
<div class="formLabel pf_formLabel">
  <label for="free_time">Time:</label>
</div>
    <div class="formField pf_formField">
';


$html .= '
  <select name="free_time" id="free_time">';
    $f1 = $method->free_start_time;
    $f2 = explode(':', $f1);
    $from = $f2[0];

    $f1 = $method->free_end_time;
    $f2 = explode(':', $f1);
    $to = $f2[0];
   
   $it = (int)$method->time_period;
   $c = 60 / $it;
   
   
   for ($i = $from; $i<=$to; $i++)
    {
  $html .= ' <option value="'.$i.':00">'.$i.':00</option>';
  for ($q = 1; $q<$c; $q++)
  {
    $j = $q*$it;
    $html .= ' <option value="'.$i.':'.$j.'">'.$i.':'.$j.'</option>';
  }
}
   
  $html .= '</select>
</div>


  </div>
 


</div>';
}
else
{
  $html .= $method->free_text;
}
$html .= '
</div>
 
  </div>
</div>
<div class="clear" style="width: 100%; float: none; clear: both;">&nbsp;</div>
';


$arr[] = $html;
}
$htmlIn[] = $arr;
return true;
   

//return $this->displayListFE ($cart, $selected, $htmlIn);
}



To render HTML that is seen from your backend, you can use this function:

/**
* @param $virtuemart_order_id
* @return string
*/
function getOrderShipmentHtml ($virtuemart_order_id) {

$db = JFactory::getDBO ();
$q = 'SELECT * FROM `' . $this->_tablename . '` '
. 'WHERE `virtuemart_order_id` = ' . (int)$virtuemart_order_id;
$db->setQuery ($q);
if (!($shipinfo = $db->loadObject ())) {
vmWarn (500, $q . " " . $db->getErrorMsg ());
return '';
}

if (!class_exists ('CurrencyDisplay')) {
require(JPATH_VM_ADMINISTRATOR . DS . 'helpers' . DS . 'currencydisplay.php');
}

$currency = CurrencyDisplay::getInstance ();
$tax = ShopFunctions::getTaxByID ($shipinfo->tax_id);
$taxDisplay = is_array ($tax) ? $tax['calc_value'] . ' ' . $tax['calc_value_mathop'] : $shipinfo->tax_id;
$taxDisplay = ($taxDisplay == -1) ? JText::_ ('COM_VIRTUEMART_PRODUCT_TAX_NONE') : $taxDisplay;

$html = '<table class="adminlist">' . "\n";
$html .= $this->getHtmlHeaderBE ();
$html .= $this->getHtmlRowBE ('WEIGHT_COUNTRIES_SHIPPING_NAME', $shipinfo->shipment_name);
$html .= $this->getHtmlRowBE ('WEIGHT_COUNTRIES_WEIGHT', $shipinfo->order_weight . ' ' . ShopFunctions::renderWeightUnit ($shipinfo->shipment_weight_unit));
$html .= $this->getHtmlRowBE ('WEIGHT_COUNTRIES_COST', $currency->priceDisplay ($shipinfo->shipment_cost));
$html .= $this->getHtmlRowBE ('WEIGHT_COUNTRIES_PACKAGE_FEE', $currency->priceDisplay ($shipinfo->shipment_package_fee));
$html .= $this->getHtmlRowBE ('WEIGHT_COUNTRIES_TAX', $taxDisplay);
$html .= '</table>' . "\n";

return $html;
}


To save any values that are important for your order/shipment selection you can use this function (from weight_countries.php)

/**
* This event is fired after the order has been stored; it gets the shipment method-
* specific data.
*
* @param int    $order_id The order_id being processed
* @param object $cart  the cart
* @param array  $order The actual order saved in the DB
* @return mixed Null when this method was not selected, otherwise true
* @author Valerie Isaksen
*/
function plgVmConfirmedOrder (VirtueMartCart $cart, $order) {


if (!($method = $this->getVmPluginMethod ($order['details']['BT']->virtuemart_shipmentmethod_id))) {
return NULL; // Another method was selected, do nothing
}
if (!$this->selectedThisElement ($method->shipment_element)) {
return FALSE;
}
$values['virtuemart_order_id'] = $order['details']['BT']->virtuemart_order_id;
$values['order_number'] = $order['details']['BT']->order_number;
$values['virtuemart_shipmentmethod_id'] = $order['details']['BT']->virtuemart_shipmentmethod_id;
$values['shipment_name'] = $this->renderPluginName ($method);
$values['order_weight'] = $this->getOrderWeight ($cart, $method->weight_unit);
$values['shipment_weight_unit'] = $method->weight_unit;
$values['shipment_cost'] = $method->cost;
$values['shipment_package_fee'] = $method->package_fee;
$values['tax_id'] = $method->tax_id;
$this->storePSPluginInternalData ($values);

return TRUE;
}



In general, if you have access to your ID of the shipping method, you should call this prior execution of any other code as this one says "if i am not selected, do nothing". To make sure that your plugin should be called. Joomla's plugin system calls all of the installed plugins automatically.


if (!($this->selectedThisByMethodId ($virtuemart_shipmentmethod_id))) {
return NULL;
}




To get instance of your object, you can use this function:

if (!($method = $this->getVmPluginMethod ($order['details']['BT']->virtuemart_shipmentmethod_id))) {
return NULL; // Another method was selected, do nothing
}


Where $method already contains the backend parameters of your shipping method.

To save values in between various pages, you can use joomla's session. To have more than one shipping method per plugin, you can use json serialization techniques - please see ups, usps or similar plugin method.
The constructor is automatically loaded by joomla even when it is not enabled in virtuemart ! therefore you should carefully check if this code is eligible for your site. The included constructor includes a javascript includes which are added when any part of the system calls

JPluginHelper::importPlugin('vmshipment');

Example of the constructor:

function __construct (& $subject, $config) {

//if (self::$_this)
//   return self::$_this;
parent::__construct ($subject, $config);
//var_dump($subject); var_dump($config); die();
$this->_loggable = TRUE;
$this->tableFields = array_keys ($this->getTableSQLFields ());
$varsToPush = $this->getVarsToPush ();
$this->setConfigParameterable ($this->_configTableFieldName, $varsToPush);
//$x = debug_backtrace();
//var_dump($x);
$document = JFactory::getDocument();
$class = get_class($document);
if ($class == 'JDocumentHTML')
if (class_exists('JHTML'))
if (!defined('pickup_helper'))
{

JHTML::script('helper.js', 'plugins/vmshipment/pickup_or_free/', false);
JHTML::stylesheet('pickup_or_free.css', 'plugins/vmshipment/pickup_or_free/', false);
//JHTML::_('behavior.calendar');

$js = ' jQuery(document).ready( function($) {
  pickups = document.getElementById("pickup_shipping");
  if ((typeof pickups != "undefined") && (pickups != null))
   pickups.click();
 
});';
$document->addScriptDeclaration($js);

define('pickup_helper', 1);
}
// self::$_this
//$this->createPluginTable($this->_tablename);
//self::$_this = $this;
}


all of the other functions in the shipment methods are called with:

if (!class_exists('vmPSPlugin')) require(JPATH_VM_PLUGINS . DS . 'vmpsplugin.php');
JPluginHelper::importPlugin('vmshipment');
$_dispatcher = JDispatcher::getInstance();
$_retValues = $_dispatcher->trigger('plgVmOnSelectCheck', array( $cart, $cart ));

$dataValid = true;
foreach ($_retValues as $_retVal) {
    if ($_retVal === true ) {// Plugin completed succesfull; nothing else to do
$cart->setCartIntoSession();
return true;
    } else if ($_retVal === false ) {
   return false;
       //$mainframe = JFactory::getApplication();
       //$mainframe->redirect(JRoute::_('index.php?option=com_virtuemart&view=cart&task=editshipment',$this->useXHTML,$this->useSSL), $_retVal);
break;
    }
}


Best Regards,
Stan
----
RuposTel.com
www.rupostel.com
Your customized checkout solution for Virtuemart

veeco

@Stan, thank you so much for your explaination...
yes i made my own plugin last month, but since its a self-learning process, i would like to ask for some insight..

your method is more reliable than mine (i modified renderPluginName(method))... i'll test it right away
Work On Doxa Web Developer Indonesia - http://doxadigital.com |

alatak

Hello Veeco

May be this can help you to go through the code
http://docs.virtuemart.net/api-vm2/

When you have multiple option on the shipment
you have to return back each option in an array

example
foreach ($responses as $response) {

$$htmlIn [$i] = '<input type="radio" name="virtuemart_shipmentmethod_id"    value="' . $method->virtuemart_shipmentmethod_id . '" ' . $checked . '>
     <label  for="' . $id . '" >';
if (!empty($method->shipment_logos)) {
$html [$i] .= $this->displayLogos ($method->shipment_logos) . ' ';
}

$$htmlIn [$i] .= '<span class="' . $this->_type . '"> ' . $ShipmentName </span>
     </label>

';
$i++;
}



WebStuff

I'm in the middle of amending the "Standard Shipping " plugin to allow free shipping for certain category or product id's.
This is my first shipment plugin attempt so I'm struggling with one bit.
All seems to be working except for storing the shipment_cost in the database.
The "Shipment Cost" value from the settings, in this case 16.67, is being saved to the database instead of 0.00 which I think I'm setting in the plgVmConfirmedOrder function
function plgVmConfirmedOrder (VirtueMartCart $cart, $order) {

if (!($method = $this->getVmPluginMethod ($order['details']['BT']->virtuemart_shipmentmethod_id))) {
return NULL; // Another method was selected, do nothing
}
if (!$this->selectedThisElement ($method->shipment_element)) {
return FALSE;
}
$free_shipment_items_only = false;
foreach ($cart->products as $product) {
if(in_array($product->virtuemart_product_id, $method->free_shipment_products) or in_array($product->virtuemart_category_id, $method->free_shipment_categories)){
$free_shipment_items_only = true;
}
else {
$free_shipment_items_only = false;
break;
}
}
if ($free_shipment_items_only) {
$method->cost = 0.00;
$method->package_fee = 0.00;
}
$values['virtuemart_order_id'] = $order['details']['BT']->virtuemart_order_id;
$values['order_number'] = $order['details']['BT']->order_number;
$values['virtuemart_shipmentmethod_id'] = $order['details']['BT']->virtuemart_shipmentmethod_id;
$values['shipment_name'] = $this->renderPluginName ($method);
$values['order_weight'] = $this->getOrderWeight ($cart, $method->weight_unit);
$values['shipment_weight_unit'] = $method->weight_unit;
$values['shipment_cost'] = $method->cost;
$values['shipment_package_fee'] = $method->package_fee;
$values['tax_id'] = $method->tax_id;
$this->storePSPluginInternalData ($values);

return TRUE;
}

I think I may be setting it in the wrong place but can't find where to set it. Any help greatly appreciated, I will be releasing this to the community if I can get it working.

Thanks for any help.

[attachment cleanup by admin]

GJC Web Design

the comment says for function plgVmConfirmedOrder

* This event is fired after the order has been stored; it gets the shipment method- specific data.

perhaps you should transfer or duplicate this logic to the function

function getCosts (VirtueMartCart $cart, $method, $cart_prices) {

if ($method->free_shipment && $cart_prices['salesPrice'] >= $method->free_shipment) {
return 0;
} else {
return $method->cost + $method->package_fee;
}
}


or do the logic in the function checkConditions ?
GJC Web Design
VirtueMart and Joomla Developers - php developers https://www.gjcwebdesign.com
VM4 AusPost Shipping Plugin - e-go Shipping Plugin - VM4 Postcode Shipping Plugin - Radius Shipping Plugin - VM4 NZ Post Shipping Plugin - AusPost Estimator
Samport Payment Plugin - EcomMerchant Payment Plugin - ccBill payment Plugin
VM2 Product Lock Extension - VM2 Preconfig Adresses Extension - TaxCloud USA Taxes Plugin - Virtuemart  Product Review Component
https://extensions.joomla.org/profile/profile/details/67210
Contact for any VirtueMart or Joomla development & customisation

WebStuff

Nope tried it in there but no go.  :(
function getCosts (VirtueMartCart $cart, $method, $cart_prices) {

$free_shipment_items_only = false;
foreach ($cart->products as $product) {
if(in_array($product->virtuemart_product_id, $method->free_shipment_products) or in_array($product->virtuemart_category_id, $method->free_shipment_categories)){
$free_shipment_items_only = true;
}
else {
$free_shipment_items_only = false;
}
}

if (($method->free_shipment && $cart_prices['salesPrice'] >= $method->free_shipment) || $free_shipment_items_only) {
$method->cost = 0;
$method->package_fee = 0;
return 0;
} else {
return $method->cost + $method->package_fee;
}
}


Even explicitly setting the $method->cost and $method->package_fee values before returning doesn't work.
The order is getting sent with free shipment showing on the invoice but obviously for accounting and so on the backend is pulling or storing this information incorrectly in the shipment method specific database.

Thanks for the reply. :)

GJC Web Design

You need to echo out what is the $order object that is sent to function plgVmConfirmedOrder (VirtueMartCart $cart, $order)

this is where the record is stored and what the admin is pulling from....

the records stored are

$method = $this->getVmPluginMethod ($order['details']['BT']->virtuemart_shipmentmethod_id)

$method->cost
$method->package_fee

is the fact that this may be returning NULL mean that an existing instance of $method->cost is being taken?

print out each stage and with a die(); so u can see exactly what's happening

print 'Debug Line '.__LINE__.' $row <pre>'; print_r ($method->cost); print "</pre><br />\n";

is the above function even being called in the plugin?
GJC Web Design
VirtueMart and Joomla Developers - php developers https://www.gjcwebdesign.com
VM4 AusPost Shipping Plugin - e-go Shipping Plugin - VM4 Postcode Shipping Plugin - Radius Shipping Plugin - VM4 NZ Post Shipping Plugin - AusPost Estimator
Samport Payment Plugin - EcomMerchant Payment Plugin - ccBill payment Plugin
VM2 Product Lock Extension - VM2 Preconfig Adresses Extension - TaxCloud USA Taxes Plugin - Virtuemart  Product Review Component
https://extensions.joomla.org/profile/profile/details/67210
Contact for any VirtueMart or Joomla development & customisation

WebStuff

Well it turned out to be something really stupid. ~Doh.
I assumed that the convert function was resetting the params to my values before save functions were called but apparently not.
So I just needed to redo the conversion to an array myself.
Problem solved. Thanks for your help, would never have spotted it otherwise.
Bit more testing and I'll package it up get it out there.

WebStuff

Okay posted the plugin here : http://forum.virtuemart.net/index.php?topic=113169
If anyone wants to test it before I submit it to the extensions websites.

veeco

In which function i can put this $htmlIn:

i tried this:
public function plgVmDisplayListFEShipment (VirtueMartCart $cart, $selected = 0, &$htmlIn) {
      $htmlIn[]="TESTING";
      return $this->displayListFE ($cart, $selected, $htmlIn);
   }

but there's no effect
Quote from: alatak on January 16, 2013, 16:18:43 PM
Hello Veeco

May be this can help you to go through the code
http://docs.virtuemart.net/api-vm2/

When you have multiple option on the shipment
you have to return back each option in an array

example
foreach ($responses as $response) {

$$htmlIn [$i] = '<input type="radio" name="virtuemart_shipmentmethod_id"    value="' . $method->virtuemart_shipmentmethod_id . '" ' . $checked . '>
     <label  for="' . $id . '" >';
if (!empty($method->shipment_logos)) {
$html [$i] .= $this->displayLogos ($method->shipment_logos) . ' ';
}

$$htmlIn [$i] .= '<span class="' . $this->_type . '"> ' . $ShipmentName </span>
     </label>

';
$i++;
}



Work On Doxa Web Developer Indonesia - http://doxadigital.com |

veeco

#13
the main idea is to give option for buyer on checkout page:


My current solution is by using renderPluginName() method, but i'm looking for proper way to do this....
because by default, i can't access the Cart and Method Object unless i set the cart and method on checkCondition:

protected function checkConditions ($cart, $method, $cart_prices) {
$this->cart=$cart;
$this->method=$method;
}
Work On Doxa Web Developer Indonesia - http://doxadigital.com |

AH

#14
This is now base functionality in vm 2.0.24

Enable OPC in Configuration/checkout

The available shipping and payment methods/rates will be shown as radio selections on the checkout page  :) :)
Regards
A

Joomla 4.4.5
php 8.1