SlideShare a Scribd company logo
Un bloc
WooTenberg
Associatif
Rémi
CORSON
HE @ Automattic
remicorson.com
@remicorson
WooTenberg
=
WooCommerce
+
Gutenberg
WigWam
AssoLoi 1901
Existant
WooCommerce
Subscriptions
Memberships
Dynamic Pricing
Groups
Mois glissants
Contraintes
Temps
Compétences techniques
Licenses
Complexité
Mises à jour
Gestion des commandes
BesoinsPrix au mois
1 mois 3 mois 6 mois
50 € 45 € 40 €
- -10% -20%
BesoinsPrix à la journée et adhésion
1 jour Adhésion
5 € 10 €
Budget
0euro
Résultat
Créer
un plugin
avec WP-CLI
# Create plugin called "Coworking Blocks"
$ wp scaffold plugin "Coworking Blocks"
Créer
un bloc
Gutenberg
avec WP-CLI
# Add a block called "Choose Plan" to plugin "Coworking Blocks"
$ wp scaffold block wigwam --title="Choose Plan" --plugin="Coworking Blocks"
Créer
les produits
2019
Avec un CSV
avec
WP-CLI
wp wc product create
--field_name="${field_name}"
Créer
les produits
2020
Création automatisée
/**
* Duplicate the product for year+1 for the corresponding month.
*
* @package      WooCommerce
* @author       Remi Corson
* @license      https://codex.wordpress.org/GPL   GNU General Public License
* @see https://docs.woocommerce.com/wc-apidocs/class-WC_Admin_Duplicate_Product.html
* @see          https://docs.woocommerce.com/wc-apidocs/source-class-WC_Product.html#1322-1345
*
* @version  1.0.0
* @since    1.0.0
*
* @return   Int  The duplicated post ID.
*/
function duplicate_month_product() {
//...
}
add_action( 'init', 'duplicate_month_product' );
Dupliquer le mois N pour créer N+1
function duplicate_month_product() {
global $this_year, $next_year;
// Check if month next year exists.
$id_exists = wc_get_product_id_by_sku( $next_year . date( 'm' ) );
// If corresponding products exists for next year, return.
if ( $id_exists ) {
return;
}
// Get the product corresponding to this month.
$product_id = wc_get_product_id_by_sku( $this_year . date( 'm' ) );
$product = wc_get_product( $product_id );
$wc_duplicate = new WC_Admin_Duplicate_Product();
$duplicated_product = $wc_duplicate->product_duplicate( wc_get_product( $product->get_ID() ) );
$duplicated_id = $duplicated_product->get_ID();
$duplicated = new WC_Product( $duplicated_id );
// Update name, replace current year by year +1.
$duplicated->set_name( str_replace( $this_year, $next_year, $product->get_name() ) );
// Update SKU, set it to year +1 followed by current month, ex: 201901.
$duplicated->set_sku( $next_year . date( 'm' ) );
// Update slug, set it to $product sku followed by year +1, ex: janvier-2019.
$duplicated->set_sku( str_replace( $this_year, $next_year, $product->get_sku() ) );
// Update menu order (position) to next year followed by month number with 0, ex: 201901.
$duplicated->set_menu_order( $next_year . date( 'm' ) );
// Update status from draft to publish.
$duplicated->set_status( 'publish' );
// Update cagtegory if needed.
$term_slug = 'plan-' . $next_year;
$term = term_exists( $term_slug, 'product_cat' ); // returns 0 or term ID.
if ( 0 !== $term && null !== $term ) {
$category_id = $term;
} else {
$category_id = wp_insert_term( $term_slug, 'product_cat' );
}
$duplicated->set_category_ids( array( $category_id['term_id'] ) );
$duplicated->save();
}
En version simplifiée
// Get product to duplicate.
wc_get_product_id_by_sku();
// Duplicate.
new WC_Admin_Duplicate_Product();
// Set SKU, menu order and term (eg: plan-2020)
$duplicated->set_sku( $next_year . date( 'm' ) );
$duplicated->set_menu_order( $next_year . date( 'm' ) );
$duplicated->set_category_ids( array( $category_id['term_id'] ) );
$duplicated->save();
Pourquoi
1 produit unique
par mois ?
Rendu
en front
$products = wc_get_products(
array(
'return' => 'objects',
'orderby' => 'menu_order',
'order' => 'ASC',
'category' => array( 'plan-' . $this_year, 'plan-' . $next_year ), // Year & year+1
'limit' => 12, // 12 sliding months.
'offset' => ( date( 'n' ) - 1 ), // not outdated months but keep current year.
)
);
$products = wc_get_products(
array(
'return' => 'objects',
'orderby' => 'menu_order',
'order' => 'ASC',
'category' => array( 'plan-' . $this_year, 'plan-' . $next_year ), // Year & year+1
'limit' => 12, // 12 sliding months.
'offset' => ( date( 'n' ) - 1 ), // not outdated months but keep current year.
)
);
en référence à
$duplicated->set_menu_order( $next_year . date( 'm' ) );
Block render en front
register_block_type( 'coworking-blocks/choose-plan', array(
'editor_script' => 'choose-plan-block-editor',
'editor_style' => 'choose-plan-block-editor',
'style' => 'choose-plan-block',
'render_callback' => 'render_block_choose_plan',
) );
Rendu
en back
Block render en back 1/3
var el = wp.element.createElement,
__ = wp.i18n.__,
registerBlockType = wp.blocks.registerBlockType,
ServerSideRender = wp.components.ServerSideRender;
Block render en back 2/3
registerBlockType( 'coworking-blocks/choose-plan', {
title: __( 'Choose Plan' ),
icon: 'calendar-alt',
category: 'layout',
render_callback: 'block_dynamic_render',
supports: {
// Removes support for an HTML mode.
html: false,
},
//...
})
);
Block render en back 3/3
edit: function( props ) {
// ensure the block attributes matches this plugin's name
return (
el(ServerSideRender, {
block: "coworking-blocks/choose-plan",
attributes: props.attributes
})
);
//...
})
);
Fonctionnement
du bloc
https://coworking-lannion.org/panier/?add-to-cart=11
&quantity[12]=1
&quantity[13]=3
&quantity[17]=1
&quantity[18]=1
&quantity[19]=1
&quantity[20]=1
&quantity[21]=1
&quantity[22]=1
https://coworking-lannion.org/panier/?add-to-cart=11
&quantity[12]=1 // Adhesion
&quantity[13]=3 // 3 tickets journée
&quantity[17]=1 // Avril
&quantity[18]=1 // Mai
&quantity[19]=1 // Juin
&quantity[20]=1 // Juillet
&quantity[21]=1 // Aout
&quantity[22]=1 // Septembre
https://coworking-lannion.org/panier/?add-to-cart=11
&quantity[12]=1 // Adhesion
&quantity[13]=3 // 3 tickets journée
&quantity[17]=1 // Avril
&quantity[18]=1 // Mai
&quantity[19]=1 // Juin
&quantity[20]=1 // Juillet
&quantity[21]=1 // Aout
&quantity[22]=1 // Septembre
add-to-cart=11
Utiliser un
produit groupé
pour ajouter 2+ produits
au panier via une URL
Appliquer la
Réduction
Coupon ou pas coupon ?
function apply_matched_coupons() {
global $this_year, $next_year;
// Count products in cart.
$products_count = get_cart_category_count( array( 'plan-' . $this_year, 'plan-' . $next_year ) );
// Set $cat_in_cart to false.
$cat_in_cart = false;
// Choose coupon to apply based on count products in cart.
switch ( $products_count ) {
case $products_count >= 3 && $products_count < 6:
$coupon_code = '10percent';
break;
case $products_count >= 6:
$coupon_code = '20percent';
break;
default:
$coupon_code = '';
break;
}
//...
}
add_discount();
// If coupon already in cart then return.
if ( WC()->cart->has_discount( $coupon_code ) ) {
return;
}
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// If Cart has category "plan-XXXX", set $cat_in_cart to true.
if ( has_term( 'plan-' . $this_year, 'product_cat', $cart_item['product_id'] )
|| has_term( 'plan-' . $next_year, 'product_cat', $cart_item['product_id'] ) ) {
$cat_in_cart = true;
}
}
// Apply coupon.
if ( true === $cat_in_cart && $products_count >= 3 ) {
WC()->cart->add_discount( $coupon_code );
}
Masquer l'application
de la Réduction
// Hide "coupon applied" notification.
add_filter( 'woocommerce_coupon_message', '__return_null' );
Ajuster les prix
dans le panier
Filter les valeurs du panier
add_filter( 'woocommerce_cart_item_subtotal', 'if_coupon_slash_item_subtotal', 99, 3 );
add_filter( 'woocommerce_cart_item_price', 'if_coupon_slash_item_subtotal', 99, 3 );
Le panier a t-il un coupon ?
function if_coupon_slash_item_subtotal( $subtotal, $cart_item, $cart_item_key ) {
global $woocommerce, $this_year, $next_year;
// Check if cart has coupon.
if ( ! $woocommerce->cart->has_discount() ) {
return $subtotal;
}
// Get coupons.
$coupons = WC()->cart->get_applied_coupons();
//...
Mettre à jour les prix des produits et le
sous-total
// Loop through coupons.
foreach ( $coupons as $coupon ) {
// Create coupon object.
$coupon = new WC_Coupon( $coupon );
// Check if specific coupon is applied.
if ( $woocommerce->cart->has_discount( $coupon->get_code() ) ) {
// Check if products in cart are from specific category (ex: plan-2019 & plan-2020).
if ( has_term( 'plan-' . $this_year, 'product_cat', $cart_item['product_id'] ) || has_term( 'plan-' . $next_year, 'product_cat', $cart_item['product_id'] ) ) {
// If coupon type is fixed_product.
if ( 'fixed_product' === $coupon->get_discount_type() ) {
$newsubtotal = wc_price( $cart_item['data']->get_price() - $coupon->get_amount() * $cart_item['quantity'] );
} else { // If coupon type is percentage.
$newsubtotal = wc_price( $cart_item['data']->get_price() * ( 1 - ( '0.' . $coupon->get_amount() ) ) * $cart_item['quantity'] );
}
$subtotal = sprintf( '<span class="discounted_price">%s</span>', $newsubtotal );
}
}
}
return $subtotal;
}
Remplacer le sous-total par le total
function filter_woocommerce_cart_subtotal( $cart_subtotal, $compound, $instance ) {
$totals = WC()->cart->get_totals();
return $totals['total'];
};
add_filter( 'woocommerce_cart_subtotal', 'filter_woocommerce_cart_subtotal', 10, 3 );
Place au
javaScript !
Mise à jour à chaque clic
jQuery('.inputGroup input').change(function(){
//...
});
Création des variables
/*
* DEFINE VARS
* -----------
*/
var total = 0;
var counter = 0;
var discount = 0;
var discountDisplay = '';
var finalUrl = jQuery( '#addToCart' ).data('url');
var addToCart = jQuery( '#addToCart' ).data('text');
var hasMembership = false;
var membershipCost = 0;
var ticketsTotal = 0;
Prix de l'adhésion
/*
* MEMBERSHIPS
* -----------
*/
jQuery('.memberships:checked').each(function(){
url = "&quantity["+jQuery(this).val()+"]=1"; // Append URL
finalUrl += url; // Create final URL
membershipCost +=parseFloat( jQuery(this).data('price') * 1 ); // Gets price
});
Prix des tickets journée
/*
* DAY TICKETS
* -----------
*/
jQuery('input[type=number][name=day_tickets]').each(function(){
url = "&quantity["+jQuery(this).data('id')+"]=" + jQuery(this).val();
finalUrl += url;
ticketsTotal = jQuery(this).data('price') * jQuery(this).val();
});
Prix des mois
/*
* MONTHLY PLAN
* ------------
*/
jQuery('.product_ids:checked').each(function(){
counter++;
// Build add to cart custom URL.
url = "&quantity["+jQuery(this).val()+"]=1";
finalUrl += url;
// Calculate total price.
price =jQuery(this).data('price') * 1;
total +=parseFloat(price);
});
Calcul de la réduction
/*
* DISCOUNT
* ----------------
*/
// Calculate discount amount.
switch( true ) {
case ( counter >= 3 && counter < 6 ):
discount = 10;
break;
case ( counter >= 6 ):
discount = 20;
break;
default:
discount = 0;
}
Calcul du prix final
/*
* FINAL PRICE CALCULATION
* -----------------------
*/
if( discount != 0 ) {
jQuery( '#final_price').val( total - (total * discount / 100) + membershipCost + ticketsTotal );
discountDisplay = 'Vous bénéficiez de <span class="discount-amount">' + discount + '%</span> de remise !';
jQuery( '.plan-discount' ).html( discountDisplay );
jQuery( '.plan-discount' ).addClass( 'discount-tada' );
} else {
jQuery( '#final_price').val( total + membershipCost + ticketsTotal );
jQuery( '.plan-discount' ).empty();
jQuery( '.plan-discount' ).removeClass( 'discount-tada' );
}
Création de l'URL finale
/*
* DISPLAY PRICE & UPDATE ADD TO CART URL
* --------------------------------------
*/
jQuery( '#final_url' ).val( finalUrl );
jQuery( '#addToCart' ).attr('href', jQuery('#final_url').val() );
if( jQuery('#final_price').val() > 0 ) {
jQuery( '#addToCart' ).text( addToCart + ' - ' + jQuery('#final_price').val() + '€');
} else {
jQuery( '#addToCart' ).text( addToCart );
}
Que
Retenir?
Définir le
BESOIN
Vous sauvera
du temps
Vous sauvera
de l'argent
Vous sauvera
des galères
Vous sauvera
des prises de tête
Vous sauverades solutions bancales et de pas mal d'autres trucs relous que je ne peux pas lister ici car je n'ai pas la place
mais que j'aurais quand même bien aimer vous lister car ça craint d'être en glère, enfin moi j'aime pas trop
même si certains aiment ça, quoi que j'en doute, mais il faut de tout dans ce monde pas vrai ? Alors évidemment
définir vos besoins ne vous sauvera pas de l'apocalypse, des pluies acides, des zombies, de la crise, ou du dernier
album de Christophe Maé, d'ailleurs ça me fait me demander si l'harmonica ça a déjà été à la mode un jour ?
Peut-être que oui au fond.... en fait je crois que tu bluffes Martony ? Je sais pas il a pas l'air de bluffer là quand même !
Vous savez où je pourrai trouver de bonnes gencives de porc ? Ha merde je crois que je m'égare un peu là...

More Related Content

Featured

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
Marius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
Expeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
Pixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
ThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
marketingartwork
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
Skeleton Technologies
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
Kurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
SpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Lily Ray
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
Rajiv Jayarajah, MAppComm, ACC
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
Christy Abraham Joy
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
Vit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
MindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
RachelPearson36
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

Un bloc WooTenberg pour un coworking associatif

  • 10. BesoinsPrix au mois 1 mois 3 mois 6 mois 50 € 45 € 40 € - -10% -20%
  • 11. BesoinsPrix à la journée et adhésion 1 jour Adhésion 5 € 10 €
  • 13. 0euro
  • 15.
  • 17. # Create plugin called "Coworking Blocks" $ wp scaffold plugin "Coworking Blocks"
  • 19. # Add a block called "Choose Plan" to plugin "Coworking Blocks" $ wp scaffold block wigwam --title="Choose Plan" --plugin="Coworking Blocks"
  • 20.
  • 21.
  • 24.
  • 25.
  • 26.
  • 28. wp wc product create
  • 29.
  • 30.
  • 31.
  • 35. /** * Duplicate the product for year+1 for the corresponding month. * * @package      WooCommerce * @author       Remi Corson * @license      https://codex.wordpress.org/GPL   GNU General Public License * @see https://docs.woocommerce.com/wc-apidocs/class-WC_Admin_Duplicate_Product.html * @see          https://docs.woocommerce.com/wc-apidocs/source-class-WC_Product.html#1322-1345 * * @version  1.0.0 * @since    1.0.0 * * @return   Int  The duplicated post ID. */ function duplicate_month_product() { //... } add_action( 'init', 'duplicate_month_product' );
  • 36. Dupliquer le mois N pour créer N+1 function duplicate_month_product() { global $this_year, $next_year; // Check if month next year exists. $id_exists = wc_get_product_id_by_sku( $next_year . date( 'm' ) ); // If corresponding products exists for next year, return. if ( $id_exists ) { return; } // Get the product corresponding to this month. $product_id = wc_get_product_id_by_sku( $this_year . date( 'm' ) ); $product = wc_get_product( $product_id ); $wc_duplicate = new WC_Admin_Duplicate_Product(); $duplicated_product = $wc_duplicate->product_duplicate( wc_get_product( $product->get_ID() ) ); $duplicated_id = $duplicated_product->get_ID(); $duplicated = new WC_Product( $duplicated_id ); // Update name, replace current year by year +1. $duplicated->set_name( str_replace( $this_year, $next_year, $product->get_name() ) ); // Update SKU, set it to year +1 followed by current month, ex: 201901. $duplicated->set_sku( $next_year . date( 'm' ) ); // Update slug, set it to $product sku followed by year +1, ex: janvier-2019. $duplicated->set_sku( str_replace( $this_year, $next_year, $product->get_sku() ) ); // Update menu order (position) to next year followed by month number with 0, ex: 201901. $duplicated->set_menu_order( $next_year . date( 'm' ) ); // Update status from draft to publish. $duplicated->set_status( 'publish' ); // Update cagtegory if needed. $term_slug = 'plan-' . $next_year; $term = term_exists( $term_slug, 'product_cat' ); // returns 0 or term ID. if ( 0 !== $term && null !== $term ) { $category_id = $term; } else { $category_id = wp_insert_term( $term_slug, 'product_cat' ); } $duplicated->set_category_ids( array( $category_id['term_id'] ) ); $duplicated->save(); }
  • 37. En version simplifiée // Get product to duplicate. wc_get_product_id_by_sku(); // Duplicate. new WC_Admin_Duplicate_Product(); // Set SKU, menu order and term (eg: plan-2020) $duplicated->set_sku( $next_year . date( 'm' ) ); $duplicated->set_menu_order( $next_year . date( 'm' ) ); $duplicated->set_category_ids( array( $category_id['term_id'] ) ); $duplicated->save();
  • 38.
  • 41.
  • 42. $products = wc_get_products( array( 'return' => 'objects', 'orderby' => 'menu_order', 'order' => 'ASC', 'category' => array( 'plan-' . $this_year, 'plan-' . $next_year ), // Year & year+1 'limit' => 12, // 12 sliding months. 'offset' => ( date( 'n' ) - 1 ), // not outdated months but keep current year. ) );
  • 43. $products = wc_get_products( array( 'return' => 'objects', 'orderby' => 'menu_order', 'order' => 'ASC', 'category' => array( 'plan-' . $this_year, 'plan-' . $next_year ), // Year & year+1 'limit' => 12, // 12 sliding months. 'offset' => ( date( 'n' ) - 1 ), // not outdated months but keep current year. ) ); en référence à $duplicated->set_menu_order( $next_year . date( 'm' ) );
  • 44. Block render en front register_block_type( 'coworking-blocks/choose-plan', array( 'editor_script' => 'choose-plan-block-editor', 'editor_style' => 'choose-plan-block-editor', 'style' => 'choose-plan-block', 'render_callback' => 'render_block_choose_plan', ) );
  • 46. Block render en back 1/3 var el = wp.element.createElement, __ = wp.i18n.__, registerBlockType = wp.blocks.registerBlockType, ServerSideRender = wp.components.ServerSideRender;
  • 47. Block render en back 2/3 registerBlockType( 'coworking-blocks/choose-plan', { title: __( 'Choose Plan' ), icon: 'calendar-alt', category: 'layout', render_callback: 'block_dynamic_render', supports: { // Removes support for an HTML mode. html: false, }, //... }) );
  • 48. Block render en back 3/3 edit: function( props ) { // ensure the block attributes matches this plugin's name return ( el(ServerSideRender, { block: "coworking-blocks/choose-plan", attributes: props.attributes }) ); //... }) );
  • 49.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 58. https://coworking-lannion.org/panier/?add-to-cart=11 &quantity[12]=1 // Adhesion &quantity[13]=3 // 3 tickets journée &quantity[17]=1 // Avril &quantity[18]=1 // Mai &quantity[19]=1 // Juin &quantity[20]=1 // Juillet &quantity[21]=1 // Aout &quantity[22]=1 // Septembre
  • 59. https://coworking-lannion.org/panier/?add-to-cart=11 &quantity[12]=1 // Adhesion &quantity[13]=3 // 3 tickets journée &quantity[17]=1 // Avril &quantity[18]=1 // Mai &quantity[19]=1 // Juin &quantity[20]=1 // Juillet &quantity[21]=1 // Aout &quantity[22]=1 // Septembre
  • 61.
  • 62.
  • 63. Utiliser un produit groupé pour ajouter 2+ produits au panier via une URL
  • 65.
  • 66. Coupon ou pas coupon ? function apply_matched_coupons() { global $this_year, $next_year; // Count products in cart. $products_count = get_cart_category_count( array( 'plan-' . $this_year, 'plan-' . $next_year ) ); // Set $cat_in_cart to false. $cat_in_cart = false; // Choose coupon to apply based on count products in cart. switch ( $products_count ) { case $products_count >= 3 && $products_count < 6: $coupon_code = '10percent'; break; case $products_count >= 6: $coupon_code = '20percent'; break; default: $coupon_code = ''; break; } //... }
  • 67. add_discount(); // If coupon already in cart then return. if ( WC()->cart->has_discount( $coupon_code ) ) { return; } foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { // If Cart has category "plan-XXXX", set $cat_in_cart to true. if ( has_term( 'plan-' . $this_year, 'product_cat', $cart_item['product_id'] ) || has_term( 'plan-' . $next_year, 'product_cat', $cart_item['product_id'] ) ) { $cat_in_cart = true; } } // Apply coupon. if ( true === $cat_in_cart && $products_count >= 3 ) { WC()->cart->add_discount( $coupon_code ); }
  • 69. // Hide "coupon applied" notification. add_filter( 'woocommerce_coupon_message', '__return_null' );
  • 71.
  • 72. Filter les valeurs du panier add_filter( 'woocommerce_cart_item_subtotal', 'if_coupon_slash_item_subtotal', 99, 3 ); add_filter( 'woocommerce_cart_item_price', 'if_coupon_slash_item_subtotal', 99, 3 );
  • 73. Le panier a t-il un coupon ? function if_coupon_slash_item_subtotal( $subtotal, $cart_item, $cart_item_key ) { global $woocommerce, $this_year, $next_year; // Check if cart has coupon. if ( ! $woocommerce->cart->has_discount() ) { return $subtotal; } // Get coupons. $coupons = WC()->cart->get_applied_coupons(); //...
  • 74. Mettre à jour les prix des produits et le sous-total // Loop through coupons. foreach ( $coupons as $coupon ) { // Create coupon object. $coupon = new WC_Coupon( $coupon ); // Check if specific coupon is applied. if ( $woocommerce->cart->has_discount( $coupon->get_code() ) ) { // Check if products in cart are from specific category (ex: plan-2019 & plan-2020). if ( has_term( 'plan-' . $this_year, 'product_cat', $cart_item['product_id'] ) || has_term( 'plan-' . $next_year, 'product_cat', $cart_item['product_id'] ) ) { // If coupon type is fixed_product. if ( 'fixed_product' === $coupon->get_discount_type() ) { $newsubtotal = wc_price( $cart_item['data']->get_price() - $coupon->get_amount() * $cart_item['quantity'] ); } else { // If coupon type is percentage. $newsubtotal = wc_price( $cart_item['data']->get_price() * ( 1 - ( '0.' . $coupon->get_amount() ) ) * $cart_item['quantity'] ); } $subtotal = sprintf( '<span class="discounted_price">%s</span>', $newsubtotal ); } } } return $subtotal; }
  • 75. Remplacer le sous-total par le total function filter_woocommerce_cart_subtotal( $cart_subtotal, $compound, $instance ) { $totals = WC()->cart->get_totals(); return $totals['total']; }; add_filter( 'woocommerce_cart_subtotal', 'filter_woocommerce_cart_subtotal', 10, 3 );
  • 77. Mise à jour à chaque clic jQuery('.inputGroup input').change(function(){ //... });
  • 78. Création des variables /* * DEFINE VARS * ----------- */ var total = 0; var counter = 0; var discount = 0; var discountDisplay = ''; var finalUrl = jQuery( '#addToCart' ).data('url'); var addToCart = jQuery( '#addToCart' ).data('text'); var hasMembership = false; var membershipCost = 0; var ticketsTotal = 0;
  • 79. Prix de l'adhésion /* * MEMBERSHIPS * ----------- */ jQuery('.memberships:checked').each(function(){ url = "&quantity["+jQuery(this).val()+"]=1"; // Append URL finalUrl += url; // Create final URL membershipCost +=parseFloat( jQuery(this).data('price') * 1 ); // Gets price });
  • 80. Prix des tickets journée /* * DAY TICKETS * ----------- */ jQuery('input[type=number][name=day_tickets]').each(function(){ url = "&quantity["+jQuery(this).data('id')+"]=" + jQuery(this).val(); finalUrl += url; ticketsTotal = jQuery(this).data('price') * jQuery(this).val(); });
  • 81. Prix des mois /* * MONTHLY PLAN * ------------ */ jQuery('.product_ids:checked').each(function(){ counter++; // Build add to cart custom URL. url = "&quantity["+jQuery(this).val()+"]=1"; finalUrl += url; // Calculate total price. price =jQuery(this).data('price') * 1; total +=parseFloat(price); });
  • 82. Calcul de la réduction /* * DISCOUNT * ---------------- */ // Calculate discount amount. switch( true ) { case ( counter >= 3 && counter < 6 ): discount = 10; break; case ( counter >= 6 ): discount = 20; break; default: discount = 0; }
  • 83. Calcul du prix final /* * FINAL PRICE CALCULATION * ----------------------- */ if( discount != 0 ) { jQuery( '#final_price').val( total - (total * discount / 100) + membershipCost + ticketsTotal ); discountDisplay = 'Vous bénéficiez de <span class="discount-amount">' + discount + '%</span> de remise !'; jQuery( '.plan-discount' ).html( discountDisplay ); jQuery( '.plan-discount' ).addClass( 'discount-tada' ); } else { jQuery( '#final_price').val( total + membershipCost + ticketsTotal ); jQuery( '.plan-discount' ).empty(); jQuery( '.plan-discount' ).removeClass( 'discount-tada' ); }
  • 84. Création de l'URL finale /* * DISPLAY PRICE & UPDATE ADD TO CART URL * -------------------------------------- */ jQuery( '#final_url' ).val( finalUrl ); jQuery( '#addToCart' ).attr('href', jQuery('#final_url').val() ); if( jQuery('#final_price').val() > 0 ) { jQuery( '#addToCart' ).text( addToCart + ' - ' + jQuery('#final_price').val() + '€'); } else { jQuery( '#addToCart' ).text( addToCart ); }
  • 91. Vous sauverades solutions bancales et de pas mal d'autres trucs relous que je ne peux pas lister ici car je n'ai pas la place mais que j'aurais quand même bien aimer vous lister car ça craint d'être en glère, enfin moi j'aime pas trop même si certains aiment ça, quoi que j'en doute, mais il faut de tout dans ce monde pas vrai ? Alors évidemment définir vos besoins ne vous sauvera pas de l'apocalypse, des pluies acides, des zombies, de la crise, ou du dernier album de Christophe Maé, d'ailleurs ça me fait me demander si l'harmonica ça a déjà été à la mode un jour ? Peut-être que oui au fond.... en fait je crois que tu bluffes Martony ? Je sais pas il a pas l'air de bluffer là quand même ! Vous savez où je pourrai trouver de bonnes gencives de porc ? Ha merde je crois que je m'égare un peu là...