Woocommerce - Cannot delete a product variation - ajax

Using WooCommerce 2.6.1
I cannot delete a product variation for a variable product: the record is still in the database after the ajax call.
It seems the ajax call doesn't go through: putting error_log(print_r('remove_variation', true)); doesn't output anything (line 387 in class-wc-ajax.php).
The action is added in the constructor of the class. The function public function remove_variation() is just not called.
Has anyone had the same issue, and found a way to make it work?

/**
* Trash a variation, don't delete it permanently.
*
* This is hooked to
* Hijack WooCommerce's WC_AJAX::remove_variation() "Delete Variation" Trash a variation if it is a subscription variation via ajax function
*/
public static function remove_variations() {
if ( isset( $_POST['variation_id'] ) ) { // removing single variation
error_log("here3");
check_ajax_referer( 'delete-variation', 'security' );
$variation_ids = array( $_POST['variation_id'] );
error_log($_POST['variation_id']);
} else { // removing multiple variations
error_log("here4");
check_ajax_referer( 'delete-variations', 'security' );
$variation_ids = (array) $_POST['variation_ids'];
}
foreach ( $variation_ids as $variation_id ) {
$variation_post = get_post( $variation_id );
error_log(print_r($variation_post, ));
if ( $variation_post && $variation_post->post_type == 'product_variation' ) {
$variation_product = get_product( $variation_id );
if ( $variation_product && $variation_product->is_type( 'subscription_variation' ) ) {
wp_trash_post( $variation_id );
}
}
}
die();
}
remove && $variation_product->is_type( 'subscription_variation' ) to solve the problem of un-deletable variations. http://support.woothemes.com/requests/162693 should provide a patch, issue has been reported.

Deleting the variation completely
This is for WooCommerece version 3+
(this code to be put in functions or a custom plugin: not Rest api)
Trashing the variation does not remove the swatch,
it just makes the swatch disabled.(which may be a litle bit embarasing)
If you don't want the variation in your product portfolio anymore, you should delete it.
If you have created your variation using recognizable string in the slug (SKU), you can use below code.
function delete_variation($product_cat="all",$Sku__search_string="",$force_delete=false,$document_it=false){
// 'posts_per_page' => -1: goes through all posts
if($product_cat=="all") {
$args = array(
'post_type' => 'product',
'posts_per_page' => -1,
);
} else {
$args = array(
'post_type' => 'product',
'posts_per_page' => -1,
'product_cat' => $product_cat
);
}
$query = new WP_Query($args);
while ( $query->have_posts() ) {
$query->the_post();
$post_id = get_the_ID();
$product = wc_get_product($post_id);
$product_id = $product->get_id(); //same as post_id
//if you want to see what you are doing put $document_it to true;
if($document_it) echo "<br>*********** $product_id ****************<br>";
if($Sku__search_string!="") {
$variations = $product->get_available_variations();
foreach($variations as $variation){
//get the SKU slug of the variation
$sku=$variation["sku"];
//get the variation id
$variation_id=$variation['variation_id'];
if($document_it) echo "varid $variation_id <br>";
//and then get the actual variation product
$var_product=wc_get_product($variation_id);
//Check if the search_string is in the slug
//You can modify this, or modify the $args above,
//if you have other criteria
if(stripos($sku,$Sku__search_string)>0){
$is_deleted=false;
//here the variation is deleted
$is_deleted=$var_product->delete($force_delete);
if($is_deleted){
if($document_it) echo "<br>Deleted: $sku";
} else {
if($document_it) echo "<br>Not deleted: $sku";
}
}
}
}
}
}
If you want to delete all variations you may use this

Related

Ajax in checkout + select2 - how to fix the address field problematic update?

For checkout I use one script to show and hide billing fields depending on the shipping way.
Earlier it worked fine, but that time - I think because of using select2 in the city field instead of the text input - the address text field is making issues.
When user writes their address not so fast, the form is starting to update too quick, can delete the new written after that short pause characters or not delete, randomly, and when I want to delete something - the select 2 can appear on the top of the page and not closing.
add_filter( 'woocommerce_update_order_review_fragments', 'awoohc_add_update_form_billing', 99 );
function awoohc_add_update_form_billing( $fragments ) {
$checkout = WC()->checkout();
ob_start();
?>
<div class="woocommerce-billing-fields__field-wrapper">
<?php
$fields = $checkout->get_checkout_fields( 'billing' );
foreach ( $fields as $key => $field ) {
if ( isset( $field['country_field'], $fields[ $field['country_field'] ] ) ) {
$field['country'] = $checkout->get_value( $field['country_field'] );
}
woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
}
?>
</div>
<?php
$art_add_update_form_billing = ob_get_clean();
$fragments['.woocommerce-billing-fields'] = $art_add_update_form_billing;
return $fragments;
}
add_filter( 'woocommerce_checkout_fields' , 'override_billing_checkout_fields', 20, 1 );
function override_billing_checkout_fields( $fields ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( 'local_pickup:1' === $chosen_methods[0] ) {
unset( $fields['billing']['billing_address_1'] );
unset( $fields['billing']['billing_city'] );
unset( $fields['billing']['billing_state'] );
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields' , 'b_override_billing_checkout_fields', 20, 1 );
function b_override_billing_checkout_fields( $fields ) {
$chosen_methods = WC()->session->get( 'chosen_shipping_methods' );
if ( 'boxberry_self:4' === $chosen_methods[0] || 'boxberry_self_after:6' === $chosen_methods[0] ) {
unset( $fields['billing']['billing_address_1'] );
}
return $fields;
}
// Just hide woocommerce billing country
add_action( 'woocommerce_before_checkout_form', 'hide_checkout_billing_country', 5 );
function hide_checkout_billing_country() {
echo '<style>#billing_country_field{display:none;}</style>';
}
add_filter('woocommerce_billing_fields', 'customize_checkout_fields', 100 );
function customize_checkout_fields( $fields ) {
if ( is_checkout() ) {
// HERE set the required key fields below
$chosen_fields = array( 'postcode', 'country', 'company','last_name', 'address_2');
foreach( $chosen_fields as $key ) {
if( isset($fields['billing_'.$key]) && $key !== 'country') {
unset($fields['billing_'.$key]); // Remove all define fields except country
}
}
}
return $fields;
}
/*
* Updating the form
*/
add_action( 'wp_footer', 'awoohc_add_script_update_shipping_method' );
function awoohc_add_script_update_shipping_method() {
if ( is_checkout() ) {
?>
<script>
jQuery(document).ready(function ($) {
$(document.body).on('updated_checkout updated_shipping_method', function (event, xhr, data) {
$('input[name^="shipping_method"]').on('change', function () {
$('.woocommerce-billing-fields__field-wrapper').block({
message: null,
overlayCSS: {
background: '#fff',
'z-index': 1000000,
opacity: 0.3
}
});
$('select#billing_city').select2();
});
var first_name = $('#billing_first_name').val(),
phone = $('#billing_phone').val(),
email = $('#billing_email').val(),
city = $('#billing_city').val(),
address_1 = $('#billing_address_1').val(),
$(".woocommerce-billing-fields__field-wrapper").html(xhr.fragments[".woocommerce-billing-fields"]);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_first_name"]').val(first_name);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_phone"]').val(phone);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_email"]').val(email);
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_city"]').val(city);
$('select#billing_city').select2();
$(".woocommerce-billing-fields__field-wrapper").find('input[name="billing_address_1"]').val(address_1);
$('.woocommerce-billing-fields__field-wrapper').unblock();
});
});
</script>
<?php
}
}
Yes, I needed to use select2 twice, in other ways it didn't work as needed. But when even I use it once, it didn't help me with that issue. I can't disable select2, the client needs it, and needs to fix changing the address.
How I replaced the city input to select
add_filter( 'woocommerce_checkout_fields' , 'override_checkout_city_fields' );
function override_checkout_city_fields( $fields ) {
// Define here in the array your desired cities (Here an example of cities)
$option_cities = array(
'city1' => 'city1',
'city2' => 'city2',
);
$fields['billing']['billing_city']['type'] = 'select';
$fields['billing']['billing_city']['options'] = $option_cities;
$fields['shipping']['shipping_city']['type'] = 'select';
$fields['shipping']['shipping_city']['options'] = $option_cities;
return $fields;
}
Or maybe it's a delivery service plugin. Maybe you see what I don't see.
I know that exist many plugins, but I need to find what's wrong manually.
I tried to /comment/ different parts of the script, so I think it's something with select, or with delicery service plugin if not it.

Creating a custom product sort option in woocommerce categories by meta if exists

I am able to create a custom product sorting option using the official woo documentation. However, when chosen, only products that have the custom meta appear. I'd like to add order by modified time if the custom meta does not exist for a product. The end result would be products with the custom meta will appear on top sorted, all products without the custom meta will appear after them sorted by modified time.
This is the function:
add_filter( 'woocommerce_get_catalog_ordering_args', 'custom_woocommerce_get_catalog_ordering_args' );
function custom_woocommerce_get_catalog_ordering_args( $args ) {
$orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) : apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
if ( 'custom_meta' == $orderby_value ) {
$args['orderby'] = 'custom_meta';
$args['order'] = 'DESC';
$args['meta_key'] = 'meta_key_num';
}
return $args;
}
add_filter( 'woocommerce_default_catalog_orderby_options', 'custom_woocommerce_catalog_orderby' );
add_filter( 'woocommerce_catalog_orderby', 'custom_woocommerce_catalog_orderby' );
function custom_woocommerce_catalog_orderby( $sortby ) {
$sortby['custom_meta'] = 'Custom';
return $sortby;
}

filter data from complex values in magento grid

Hi I have added a new column in a shipment grid as below
$this->addColumn('telephone', array(
'header' => Mage::helper('sales')->__('Billing Phone'),
'index' => 'telephone',
'renderer'=> new OSP_Adminhtml_Block_Customer_Renderer_BillingPhone()
));
in this i am using a renderer to show custom values as below
public function render(Varien_Object $row) {
$customer_id = $row->getData('customer_id');
if( $customer_id > 0 ) {
// get member_id (club canon)
$customer = Mage::getModel('customer/customer')->load($customer_id);
if( is_object( $customer ) ) {
$value = $customer->getData('mobile');
}
}else{
$id = $row->getData('order_increment_id');
$order = Mage::getModel('sales/order')->loadByIncrementId($id);
$value = $order->getBillingAddress()->getTelephone();
}
return $value;
}
which is working fine and it shows data properly on the basis of
condition in renderer.
But the problem is now I need to filter the data also which is not
working as it looks for data in only one column as telephone or mobile
I have read about filter_condition_callback but unable to make the work . Can you please suggest me how can I make this work.
Thanks in advance
1.Add line filter_condition_callback to column, like that:
$this->addColumn('telephone', array(
'header' => Mage::helper('sales')->__('Billing Phone'),
'index' => 'telephone',
'filter_condition_callback' => array($this, '_someCallBackFunction'),
'renderer'=> new OSP_Adminhtml_Block_Customer_Renderer_BillingPhone()
));
After you added this line in your column, Magento will call this callback function, look app/code/core/Mage/Adminhtml/Block/Widget/Grid.php in 468 line.
2.In your block grid file create _someCallBackFunction function, which will take two parameters, $collection - collection object not loaded, and $column Mage_Adminhtml_Block_Widget_Grid_Column
protected function _someCallBackFunction($collection, $column)
{
$value = $column->getFilter()->getValue();
if ($value === null) { //here check if filter is not null
return $this;
}
/**
* Here you can add filter to collection
* or do other manipulations with collection.
* As example you can check filter value and filter collection.
*/
if ($value != 0) {
$collection->addFieldToFilter('telephone', array('neq' => 0));
}
return $this;
}

Add custom options while adding grouped product to cart

I've got a problem when adding a grouped product to cart.
I need to set a custom option for all products that are added to cart while adding a grouped product to cart.
What I have tried last (with a little bit of success):
<checkout_cart_product_add_after>
<observers>
<customoptions>
<type>singleton</type>
<class>Company_CustomOptions_Model_Observer</class>
<method>addCustomOptionGroupSku</method>
</customoptions>
</observers>
</checkout_cart_product_add_after>
and
public function addCustomOptionGroupSku(Varien_Event_Observer $observer) {
$product = $observer->getProduct ();
if ($product->isGrouped ()) {
$quoteItem = $observer->getQuoteItem ();
$additionalOptions = array (
'options' => array (
'label' => 'GROUPSKU',
'value' => $product->getSku ()
)
);
$quoteItem->addOption ( new Varien_Object ( array (
'product' => $quoteItem->getProduct (),
'code' => 'additional_options',
'value' => serialize ( $additionalOptions )
) ) );
}
}
I have created one grouped product, containing two products.
But that code only adds the custom option "GROUPSKU" to one of the items in the cart. The other one is untouched.
How do I get all the QuoteItems that are about to be added to the cart?
PS: I have also added this question to the Magento part of StackExchange: https://magento.stackexchange.com/questions/51883/add-custom-options-while-adding-grouped-product-to-cart
I found a solution which does not need an observer.
I had to do a rewrite to Mage_Sales_Model_Quote. More specific the method addProductAdvanced()
Bad news: You can not simply use parent::addProductAdvanced() and then do your own stuff. You have to copy the original code and fill it up with your code.
Here is what I did (look out for the comment starting with /********):
public function addProductAdvanced(Mage_Catalog_Model_Product $product, $request = null, $processMode = null) {
if ($request === null) {
$request = 1;
}
if (is_numeric ( $request )) {
$request = new Varien_Object ( array (
'qty' => $request
) );
}
if (! ($request instanceof Varien_Object)) {
Mage::throwException ( Mage::helper ( 'sales' )->__ ( 'Invalid request for adding product to quote.' ) );
}
$cartCandidates = $product->getTypeInstance ( true )->prepareForCartAdvanced ( $request, $product, $processMode );
/**
* Error message
*/
if (is_string ( $cartCandidates )) {
return $cartCandidates;
}
/**
* If prepare process return one object
*/
if (! is_array ( $cartCandidates )) {
$cartCandidates = array (
$cartCandidates
);
}
$parentItem = null;
$errors = array ();
$items = array ();
foreach ( $cartCandidates as $candidate ) {
// Child items can be sticked together only within their parent
$stickWithinParent = $candidate->getParentProductId () ? $parentItem : null;
$candidate->setStickWithinParent ( $stickWithinParent );
$item = $this->_addCatalogProduct ( $candidate, $candidate->getCartQty () );
/******** own modification for custom option GROUPSKU*/
if($product->isGrouped()){
$additionalOptions = array (
'options' => array (
'label' => 'GROUPSKU',
'value' => $product->getSku ()
)
);
$item->addOption ( new Varien_Object ( array (
'product' => $item->getProduct (),
'code' => 'additional_options',
'value' => serialize ( $additionalOptions )
) ) );
}
/******** own modification end*/
if ($request->getResetCount () && ! $stickWithinParent && $item->getId () === $request->getId ()) {
$item->setData ( 'qty', 0 );
}
$items [] = $item;
/**
* As parent item we should always use the item of first added product
*/
if (! $parentItem) {
$parentItem = $item;
}
if ($parentItem && $candidate->getParentProductId ()) {
$item->setParentItem ( $parentItem );
}
/**
* We specify qty after we know about parent (for stock)
*/
$item->addQty ( $candidate->getCartQty () );
// collect errors instead of throwing first one
if ($item->getHasError ()) {
$message = $item->getMessage ();
if (! in_array ( $message, $errors )) { // filter duplicate messages
$errors [] = $message;
}
}
}
if (! empty ( $errors )) {
Mage::throwException ( implode ( "\n", $errors ) );
}
Mage::dispatchEvent ( 'sales_quote_product_add_after', array (
'items' => $items
) );
return $item;
}
This way all the products that are added to the cart using a grouped product now have the custom option.

Incorrect Signature in facebook stream_publish call

I am having a facebook error on stream_publish call. I actually used an extension for Magento for Fconnect. Fconnect & Flogin is working fine. But it is requirement that when user place an order it should be posted on user's wall. For that I have implemented like this
document.observe('click', function(e){
if (e.element().match('a[rel^=facebook-connect]') || e.element().match('button[rel^=facebook-connect]')) {
e.stop();
FB.login(function(response){
if(response.status=='connected') setLocation('http://staging.mystore.com/facebook/customer_account/connect/');
}, {perms:"email,publish_stream"});
}
});
in Facebook Client file generateSignature method is like this
private function _generateSig($params_array)
{
Mage::log($params_array);
$str = '';
ksort($params_array);
foreach ($params_array as $k=>$v) {
$str .= "$k=$v";
}
$str .= $this->_secret;
Mage::log($str);
Mage::log('md5 sigs:: ' . md5($str));
return md5($str);
}
& My code that is calling the API is like this
$message = 'just placed an order on mystore.com';
$attachment = array(
'name' => "mystore",
'href' => 'http://www.mystore.com/',
'description' => 'New order on mystore.com',
'media' => array(array('type' => 'image',
'src' => 'http://www.mystore.com/skin/frontend/default/mystore/images/logo.png',
'href' => 'http://www.mystore.com/')));
$action_links = array( array('text' => 'Buy#mystore', 'href' => 'http://www.mystore.com/'));
$attachment = json_encode($attachment);
$action_links = json_encode($action_links);
try{
// if( $facebook->api_client->stream_publish($message, $attachment, $action_links, null, $target_id))
if($this->_getClient()->call( 'facebook.stream.publish',
array($message, $attachment, $action_links,
$this->_getClient()->users->getLoggedInUser(),
Mage::getSingleton('facebook/config')->getApiKey() )
) )
{
Mage::log( "Added on FB Wall" );
}
} catch(Exception $e)
{
Mage::log( "Exception in wall write" );
Mage::log($e);
}
After logging the Signature I found in log is
api_key=XXXXXXXXmethod=facebook.stream.publishsession_key=2.AQCm5fABfobInAS5.3600.1309352400.1-1000025660978090=just placed an order on mystore.comcall_id=1309345883.3068format=JSONv=1.01={"name":"mystore","href":"http:\/\/www.mystore.com\/","description":"New order on mystore.com","media":[{"type":"image","src":"http:\/\/www.mystore.com\/skin\/frontend\/default\/mystore\/images\/logo.png","href":"http:\/\/www.mystore.com\/"}]}2=[{"text":"Buy#mystore","href":"http:\/\/www.mystore.com\/"}]3=1000025660978094=5070afefb42b162aff748f55ecf44d110d9e2a90117ee1704e2adb41f1d190fa
I have never done any development on Facebook SO I have no Idea what to do? Please help me with solution. & let me know if u guys need any other info to understand this.
Oh yeah One more thing the Client File code that is calling Api (call method) its like this
private function _prepareParams($method, $params)
{
$defaultParams = array(
'api_key' => $this->_apiKey,
'call_id' => microtime(true),
'format' => 'JSON',
'v' => '1.0'
);
if($this->_sessionKey){
$defaultParams['session_key'] = $this->_sessionKey;
}
$params = array_merge($defaultParams, $params);
foreach ($params as $key => &$val) {
if (!is_array($val)) continue;
$val = Zend_Json::encode($val);
}
$params['method'] = $method;
if(isset($params['sig'])) {
unset($params['sig']);
}
$params['sig'] = $this->_generateSig($params);
return $params;
}
public function call($method, $args=array())
{
Mage::log($args);
$params = $this->_prepareParams($method, $args);
$client = self::_getHttpClient()
->setUri(self::FACEBOOK_REST_URI)
->setMethod(Zend_Http_Client::POST)
->resetParameters()
->setParameterPost($params);
try {
$response = $client->request();
} catch(Exception $e) {
throw new Mage_Core_Exception('Service unavaliable');
}
if(!$response->isSuccessful()) {
throw new Mage_Core_Exception('Service unavaliable');
}
$result = Zend_Json::decode($response->getBody());
//json decode returns float on long uid number? is_json check? old php?
if(is_float($result)){
$result = $response->getBody();
}
if(is_array($result) && isset($result['error_code'])) {
throw new Mage_Core_Exception($result['error_msg'], $result['error_code']);
}
return $result;
}
For calling API I used two ways $this->_getClient()->call( 'facebook.stream.publish',
& $this->_getClient()->call( 'stream_publish',
None of them are working
ok GUys I figure out the mistake
look at my code
format=JSONv=1.01={"name":"mystore","href":"http:\/\/www.mystore.com\/","description":"New order on mystore.com","media":[{"type":"image","src":"http:\/\/www.mystore.com\/skin\/frontend\/default\/mystore\/images\/logo.png","href":"http:\/\/www.mystore.com\/"}]}2=[{"text":"Buy#mystore","href":"http:\/\/www.mystore.com\/"}]3=1000025660978094=5070afefb42b162aff748f55ecf44d110d9e2a90117ee1704e2adb41f1d190fa
where u can see format=JSONv=1.01={....}2=[{.....}] the problem was I used numeric arrays for parameters. they should be associated arrays
like message={new order}attachment={....}
Once I fixed the associative array problem my code start working correctly
here is a link that'll give u detail about parameters to pass to stream.publish
http://schoolout.net/en/developers/view/39
Hope this will help somebody else too.

Resources