Plugin Directory

Changeset 3169593


Ignore:
Timestamp:
10/15/2024 08:57:35 PM (18 months ago)
Author:
acteamintegrations
Message:

Version 2.7.8

Location:
activecampaign-for-woocommerce/trunk
Files:
16 edited

Legend:

Unmodified
Added
Removed
  • activecampaign-for-woocommerce/trunk/README.txt

    r3151035 r3169593  
    44Requires at least: 6.0
    55Tested up to: 6.6.2
    6 Stable tag: 2.7.7
     6Stable tag: 2.7.8
    77Requires PHP: 7.4
    88License: GPLv2 or later
     
    6868
    6969= WooCommerce Compatibility =
    70 * Tested up to version: 9.2.3
     70* Tested up to version: 9.3.3
    7171* Minimal version requirement: 7.4.0
    7272* HPOS Compatible
     
    9494
    9595== Changelog ==
     96
     97= 2.7.8 2024-10-15 =
     98* Bugfix - WooCommerce hook for stripe added to the order sync
     99* Bugfix - Order status changes should not get lost if done quickly
     100* Bugfix - Added debug display items for product sync
     101* Bugfix - Fixed product sync issue related to gathering records due to WC updates
    96102
    97103= 2.7.7 2024-09-11 =
  • activecampaign-for-woocommerce/trunk/ac_vendor/autoload.php

    r3151035 r3169593  
    55require_once __DIR__ . '/composer/autoload_real.php';
    66
    7 return ComposerAutoloaderInitd890cba30b9ecdc77ec51b42cb0a1937::getLoader();
     7return ComposerAutoloaderInit5559985682c8e8debf7e2ee247bd1345::getLoader();
  • activecampaign-for-woocommerce/trunk/ac_vendor/composer/autoload_real.php

    r3151035 r3169593  
    33// autoload_real.php @generated by Composer
    44
    5 class ComposerAutoloaderInitd890cba30b9ecdc77ec51b42cb0a1937
     5class ComposerAutoloaderInit5559985682c8e8debf7e2ee247bd1345
    66{
    77    private static $loader;
     
    2525        require __DIR__ . '/platform_check.php';
    2626
    27         spl_autoload_register(array('ComposerAutoloaderInitd890cba30b9ecdc77ec51b42cb0a1937', 'loadClassLoader'), true, true);
     27        spl_autoload_register(array('ComposerAutoloaderInit5559985682c8e8debf7e2ee247bd1345', 'loadClassLoader'), true, true);
    2828        self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(\dirname(__FILE__)));
    29         spl_autoload_unregister(array('ComposerAutoloaderInitd890cba30b9ecdc77ec51b42cb0a1937', 'loadClassLoader'));
     29        spl_autoload_unregister(array('ComposerAutoloaderInit5559985682c8e8debf7e2ee247bd1345', 'loadClassLoader'));
    3030
    3131        $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
     
    3333            require __DIR__ . '/autoload_static.php';
    3434
    35             call_user_func(\Composer\Autoload\ComposerStaticInitd890cba30b9ecdc77ec51b42cb0a1937::getInitializer($loader));
     35            call_user_func(\Composer\Autoload\ComposerStaticInit5559985682c8e8debf7e2ee247bd1345::getInitializer($loader));
    3636        } else {
    3737            $map = require __DIR__ . '/autoload_namespaces.php';
     
    5454
    5555        if ($useStaticLoader) {
    56             $includeFiles = Composer\Autoload\ComposerStaticInitd890cba30b9ecdc77ec51b42cb0a1937::$files;
     56            $includeFiles = Composer\Autoload\ComposerStaticInit5559985682c8e8debf7e2ee247bd1345::$files;
    5757        } else {
    5858            $includeFiles = require __DIR__ . '/autoload_files.php';
    5959        }
    6060        foreach ($includeFiles as $fileIdentifier => $file) {
    61             composerRequired890cba30b9ecdc77ec51b42cb0a1937($fileIdentifier, $file);
     61            composerRequire5559985682c8e8debf7e2ee247bd1345($fileIdentifier, $file);
    6262        }
    6363
     
    6666}
    6767
    68 function composerRequired890cba30b9ecdc77ec51b42cb0a1937($fileIdentifier, $file)
     68function composerRequire5559985682c8e8debf7e2ee247bd1345($fileIdentifier, $file)
    6969{
    7070    if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
  • activecampaign-for-woocommerce/trunk/ac_vendor/composer/autoload_static.php

    r3151035 r3169593  
    55namespace Composer\Autoload;
    66
    7 class ComposerStaticInitd890cba30b9ecdc77ec51b42cb0a1937
     7class ComposerStaticInit5559985682c8e8debf7e2ee247bd1345
    88{
    99    public static $files = array (
     
    487487    {
    488488        return \Closure::bind(function () use ($loader) {
    489             $loader->prefixLengthsPsr4 = ComposerStaticInitd890cba30b9ecdc77ec51b42cb0a1937::$prefixLengthsPsr4;
    490             $loader->prefixDirsPsr4 = ComposerStaticInitd890cba30b9ecdc77ec51b42cb0a1937::$prefixDirsPsr4;
    491             $loader->classMap = ComposerStaticInitd890cba30b9ecdc77ec51b42cb0a1937::$classMap;
     489            $loader->prefixLengthsPsr4 = ComposerStaticInit5559985682c8e8debf7e2ee247bd1345::$prefixLengthsPsr4;
     490            $loader->prefixDirsPsr4 = ComposerStaticInit5559985682c8e8debf7e2ee247bd1345::$prefixDirsPsr4;
     491            $loader->classMap = ComposerStaticInit5559985682c8e8debf7e2ee247bd1345::$classMap;
    492492
    493493        }, null, ClassLoader::class);
  • activecampaign-for-woocommerce/trunk/activecampaign-for-woocommerce.php

    r3151035 r3169593  
    1717 * Plugin URI:           https://www.activecampaign.com/
    1818 * Description:          Add Abandoned Cart functionality to your WooCommerce store, synchronize order & customer information using ActiveCampaign.
    19  * Version:              2.7.7
     19 * Version:              2.7.8
    2020 * WC requires at least: 7.4.0
    21  * WC tested up to:      9.2.3
     21 * WC tested up to:      9.3.3
    2222 * Requires at least:    6.0
    2323 * Requires PHP:         7.4
  • activecampaign-for-woocommerce/trunk/admin/class-activecampaign-for-woocommerce-admin-product-sync.php

    r2966088 r3169593  
    2424 */
    2525trait Activecampaign_For_Woocommerce_Admin_Product_Sync {
     26    use Activecampaign_For_Woocommerce_Admin_Utilities;
    2627
    2728    /**
     
    6566            );
    6667
     68            $data['products']     = $this->get_products_by_offset( -1, 15, true );
    6769            $data['event_status'] = wp_get_scheduled_event( ACTIVECAMPAIGN_FOR_WOOCOMMERCE_RUN_PRODUCT_SYNC_NAME );
    6870            $data['page_url']     = esc_url( admin_url( 'admin.php?page=' . ACTIVECAMPAIGN_FOR_WOOCOMMERCE_PLUGIN_NAME_SNAKE . '_product_sync&activesync=1' ) );
  • activecampaign-for-woocommerce/trunk/admin/views/activecampaign-for-woocommerce-product-sync.php

    r3002947 r3169593  
    11<?php
    2 
     2if ( ! isset( $activecampaign_for_woocommerce_product_sync_data['products'] ) || empty( $activecampaign_for_woocommerce_product_sync_data['products'] ) ) {
     3    $activecampaign_for_woocommerce_product_sync_data['products'] = 0;
     4}
    35/**
    46 * Provide an admin product sync view for the plugin
     
    4042
    4143                            <div class="mb-500">
    42                                 Product data sync times vary based on the amount of product you're syncing in. you could check back later when sync is completed
     44                                Product data sync times vary based on the amount of product you're syncing in. you could check back later when sync is completed.<br/>
     45                                Product types that will not sync: Grouped, unpublished, private, pending, password protected
     46                            </div>
     47                            <div class="mb-500">
     48                                There are <?php echo esc_html( count( $activecampaign_for_woocommerce_product_sync_data['products'] ) ); ?> products available to sync.
    4349                            </div>
    4450
     
    8490                <div id="sync-run-section">
    8591                    <div id="activecampaign-product-sync-run-shortly" class="sync-run-status" style="border:1px dashed #2271b1; padding:5px 10px; display:none;">
    86                         <span></span>
     92                        <span>-</span>
    8793                        <?php
    8894                        esc_html_e(
     
    132138                    <?php if ( $activecampaign_for_woocommerce_product_sync_data['options']['ac_debug'] ) : ?>
    133139                        <div>
    134                             <p>Debug info: <span id="activecampaign-run-product-sync-debug">placeholder</span>
    135                             </p>
     140                            <p>Debug info: <span id="activecampaign-run-product-sync-debug">placeholder</span></p>
    136141                        </div>
    137142                    <?php endif; ?>
     
    148153                    </div>
    149154                </div>
    150 
    151155            </div>
    152156        </div>
     157        <?php
     158        if ( $activecampaign_for_woocommerce_product_sync_data['options']['ac_debug'] && isset( $activecampaign_for_woocommerce_product_sync_data['products'] ) && ! empty( $activecampaign_for_woocommerce_product_sync_data['products'] ) ) :
     159            ;
     160            ?>
     161        <div class="card max-w-none">
     162            <p>(Debug) Product IDs visible by the plugin to sync</p>
     163            <p style=""><?php echo esc_html( implode( ', ', $activecampaign_for_woocommerce_product_sync_data['products'] ) ); ?></p>
     164        </div>
     165        <?php endif; ?>
    153166    </section>
    154167</div>
  • activecampaign-for-woocommerce/trunk/includes/class-activecampaign-for-woocommerce.php

    r3128320 r3169593  
    594594            $this->order_events,
    595595            'execute_order_updated',
    596             20,
     596            90,
    597597            1
    598598        );
     
    601601            'woocommerce_order_status_changed',
    602602            $this->order_events,
    603             'execute_order_updated',
    604             20
     603            'execute_order_status_changed',
     604            10,
     605            3
     606        );
     607
     608        // needed for stripe
     609        $this->loader->add_action(
     610            'woocommerce_order_edit_status',
     611            $this->order_events,
     612            'execute_order_edit_status',
     613            20,
     614            2
    605615        );
    606616
     
    612622            2
    613623        );
     624
    614625        // On order refund
    615626        $this->loader->add_action(
    616627            'woocommerce_order_refunded',
    617628            $this->order_events,
    618             'execute_order_updated',
     629            'execute_order_updated_refund',
    619630            20,
    620             1
     631            2
    621632        );
    622633
     
    637648            $this->new_order_sync,
    638649            'execute',
     650            1,
     651            2
     652        );
     653
     654        $this->loader->add_action(
     655            'activecampaign_for_woocommerce_admin_sync_single_order_status',
     656            $this->new_order_sync,
     657            'execute_from_status',
    639658            1,
    640659            2
  • activecampaign-for-woocommerce/trunk/includes/config/activecampaign-for-woocommerce-global-constants.php

    r3151035 r3169593  
    2626 */
    2727if ( ! defined( 'ACTIVECAMPAIGN_FOR_WOOCOMMERCE_VERSION' ) ) {
    28     define( 'ACTIVECAMPAIGN_FOR_WOOCOMMERCE_VERSION', '2.7.7' );
     28    define( 'ACTIVECAMPAIGN_FOR_WOOCOMMERCE_VERSION', '2.7.8' );
    2929}
    3030
  • activecampaign-for-woocommerce/trunk/includes/orders/class-activecampaign-for-woocommerce-new-order-created-event.php

    r3089566 r3169593  
    545545        $this->schedule_recurring_order_sync_task();
    546546    }
     547
    547548}
  • activecampaign-for-woocommerce/trunk/includes/orders/class-activecampaign-for-woocommerce-new-order-sync-job.php

    r2968938 r3169593  
    112112
    113113    /**
    114      * Sync any new/live orders.
     114     * Sync any orders triggered from a status update.
    115115     * Triggered from a hook call.
    116116     *
    117117     * @param     mixed ...$args The passed args.
    118118     */
    119     public function execute( ...$args ) {
    120         if ( ! $this->logger ) {
    121             $this->logger = new Logger();
    122         }
    123 
    124         $unsynced_order_data = null;
    125         $recovered_orders    = null;
     119    public function execute_from_status( ...$args ) {
     120        $wc_order_status = null;
    126121
    127122        try {
     
    138133            }
    139134
    140             if ( isset( $wc_order_id ) ) {
    141                 // We're just syncing one row by order id
    142                 $wc_order = $this->get_wc_order( $wc_order_id );
    143 
    144                 if ( false === $wc_order || ! $this->order_has_required_data( $wc_order ) ) {
    145                     $this->mark_order_as_incompatible( $wc_order_id );
    146                     return false;
    147                 }
    148 
    149                 $ac_customer_id = $this->get_ac_customer_id( $wc_order->get_billing_email() );
    150                 $ac_order       = $this->single_sync_cofe_data( $wc_order, false, $ac_customer_id );
    151 
    152                 if ( ! isset( $ac_order ) || ! $ac_order ) {
    153                     $this->logger->warning(
    154                         'The order may have failed to sync to cofe',
    155                         [
    156                             $ac_order,
    157                         ]
    158                     );
    159 
    160                     $this->mark_order_as_failed( $wc_order_id );
    161                 } else {
    162                     $ac_id = null;
    163 
    164                     if ( self::validate_object( $ac_order, 'get_id' ) ) {
    165                         $ac_id = $ac_order->get_id();
    166                     }
    167 
    168                     $this->add_update_notes( $wc_order_id, $ac_id, $wc_order->get_status() );
    169                     $this->mark_single_order_synced( $wc_order_id );
    170                     $this->update_last_order_sync();
    171                 }
     135            // get status
     136            if ( isset( $args[1] ) && is_string( $args[1] ) ) {
     137                $wc_order_status = $args[1];
     138            }
     139
     140            if ( isset( $args[0]['status'] ) ) {
     141                $wc_order_status = $args[0]['status'];
     142            }
     143
     144            if ( isset( $args['status'] ) ) {
     145                $wc_order_status = $args['status'];
    172146            }
    173147        } catch ( Throwable $t ) {
     
    184158            }
    185159        }
     160        try {
     161            if ( isset( $wc_order_id ) ) {
     162                // We're just syncing one row by order id
     163                $wc_order = $this->get_wc_order( $wc_order_id );
     164
     165                if ( false === $wc_order || ! $this->order_has_required_data( $wc_order ) ) {
     166                    $this->mark_order_as_incompatible( $wc_order_id );
     167
     168                    return false;
     169                }
     170
     171                $this->add_meta_to_order( $wc_order );
     172
     173                $ac_customer_id = $this->get_ac_customer_id( $wc_order->get_billing_email() );
     174                $ac_order       = $this->single_sync_cofe_data( $wc_order, false, $ac_customer_id, $wc_order_status );
     175
     176                if ( ! isset( $ac_order ) || ! $ac_order ) {
     177                    $this->logger->warning(
     178                        'The order may have failed to sync to cofe',
     179                        [
     180                            $ac_order,
     181                        ]
     182                    );
     183
     184                    $this->mark_order_as_failed( $wc_order_id );
     185                } else {
     186                    $ac_id = null;
     187
     188                    if ( self::validate_object( $ac_order, 'get_id' ) ) {
     189                        $ac_id = $ac_order->get_id();
     190                    }
     191
     192                    $this->add_update_notes( $wc_order_id, $ac_id, $wc_order_status );
     193                    $this->mark_single_order_synced( $wc_order_id );
     194                    $this->update_last_order_sync();
     195                }
     196            }
     197        } catch ( Throwable $t ) {
     198            $this->logger->warning(
     199                'Activecampaign_For_Woocommerce_New_Order_Sync: There was an error processing the order data.',
     200                [
     201                    'message'     => $t->getMessage(),
     202                    'stack_trace' => $t->getTrace(),
     203                ]
     204            );
     205        }
     206    }
     207
     208    /**
     209     * Sync any new/live orders.
     210     * Triggered from a hook call.
     211     *
     212     * @param     mixed ...$args The passed args.
     213     */
     214    public function execute( ...$args ) {
     215        if ( ! $this->logger ) {
     216            $this->logger = new Logger();
     217        }
     218
     219        $unsynced_order_data = null;
     220        $recovered_orders    = null;
     221
     222        try {
     223            if ( isset( $args[0] ) && is_int( $args[0] ) ) {
     224                $wc_order_id = $args[0];
     225            }
     226
     227            if ( isset( $args[0]['wc_order_id'] ) ) {
     228                $wc_order_id = $args[0]['wc_order_id'];
     229            }
     230
     231            if ( isset( $args['wc_order_id'] ) ) {
     232                $wc_order_id = $args['wc_order_id'];
     233            }
     234
     235            if ( isset( $wc_order_id ) ) {
     236                // We're just syncing one row by order id
     237                $wc_order = $this->get_wc_order( $wc_order_id );
     238
     239                if ( false === $wc_order || ! $this->order_has_required_data( $wc_order ) ) {
     240                    $this->mark_order_as_incompatible( $wc_order_id );
     241                    return false;
     242                }
     243
     244                $ac_customer_id = $this->get_ac_customer_id( $wc_order->get_billing_email() );
     245                $ac_order       = $this->single_sync_cofe_data( $wc_order, false, $ac_customer_id );
     246
     247                if ( ! isset( $ac_order ) || ! $ac_order ) {
     248                    $this->logger->warning(
     249                        'The order may have failed to sync to cofe',
     250                        [
     251                            $ac_order,
     252                        ]
     253                    );
     254
     255                    $this->mark_order_as_failed( $wc_order_id );
     256                } else {
     257                    $ac_id = null;
     258
     259                    if ( self::validate_object( $ac_order, 'get_id' ) ) {
     260                        $ac_id = $ac_order->get_id();
     261                    }
     262
     263                    $this->add_update_notes( $wc_order_id, $ac_id, $wc_order->get_status() );
     264                    $this->mark_single_order_synced( $wc_order_id );
     265                    $this->update_last_order_sync();
     266                }
     267            }
     268        } catch ( Throwable $t ) {
     269            $this->logger->error(
     270                'Activecampaign_For_Woocommerce_New_Order_Sync: There was an error processing the order data by wc_order_id.',
     271                [
     272                    'args'        => $args,
     273                    'message'     => $t->getMessage(),
     274                    'stack_trace' => $t->getTrace(),
     275                ]
     276            );
     277            if ( isset( $wc_order_id ) && ! empty( $wc_order_id ) ) {
     278                $this->mark_order_as_incompatible( $wc_order_id );
     279            }
     280        }
    186281
    187282        try {
     
    320415                        }
    321416
     417                        $this->add_meta_to_order( $wc_order );
     418
    322419                        $this->add_update_notes( $prep_order->wc_order_id, $ac_id, $wc_order->get_status() );
    323420
     
    380477                    }
    381478
     479                    $this->add_meta_to_order( $wc_order );
    382480                    $this->add_update_notes( $unsynced_recovered_order->wc_order_id, $ac_id, $wc_order->get_status() );
    383481
     
    398496     * @return false|void
    399497     */
    400     public function single_sync_cofe_data( $wc_order, $externalcheckoutid = false, $ac_customer_id = null ) {
     498    public function single_sync_cofe_data( $wc_order, $externalcheckoutid = false, $ac_customer_id = null, $status = null ) {
    401499        if ( ! isset( $wc_order ) ) {
    402500            return false;
     
    416514            // Data for cofe order sync
    417515            $ecom_order = $cofe_order_builder->setup_cofe_order_from_table( $wc_order, 1 );
    418 
     516            if ( ! is_null( $status ) ) {
     517                $ecom_order->set_wc_status( $status );
     518            }
    419519            if ( is_null( $ecom_order ) ) {
    420520                $this->logger->warning( 'Setup COFE order returned null. Something may have gone wrong or the data may not be missing/incompatible with AC.' );
     
    548648                $data['ac_order_id']  = $ac_order_id;
    549649                $data['synced_to_ac'] = self::STATUS_SYNCED;
     650
    550651                $this->add_update_notes( $unsynced_order->wc_order_id, $ac_order_id );
    551652                $this->update_last_order_sync();
     
    718819        return $ac_customer_id;
    719820    }
     821
     822    /**
     823     * @param WC_Order $wc_order The WooCommerce order object.
     824     */
     825    private function add_meta_to_order( $wc_order ) {
     826        // save the status so update checks do not sync the same data
     827        $wc_order->add_meta_data( 'ac_last_synced_status', $wc_order->get_status(), true );
     828
     829        $last_sync_time = $wc_order->get_meta( 'ac_order_last_synced_time' );
     830        $ac_datahash    = $wc_order->get_meta( 'ac_datahash' );
     831
     832        if ( ! empty( $last_sync_time ) ) {
     833            $wc_order->update_meta_data( 'ac_order_last_synced_time', time() );
     834        } else {
     835            $wc_order->add_meta_data( 'ac_order_last_synced_time', time(), true );
     836        }
     837
     838        if ( ! empty( $ac_datahash ) ) {
     839            $wc_order->update_meta_data( 'ac_datahash', md5( json_encode( $wc_order->get_data() ) ) );
     840        } else {
     841            $wc_order->add_meta_data( 'ac_datahash', md5( json_encode( $wc_order->get_data() ) ), true );
     842        }
     843
     844        $wc_order->save_meta_data();
     845    }
    720846}
  • activecampaign-for-woocommerce/trunk/includes/orders/class-activecampaign-for-woocommerce-order-action-events.php

    r3128320 r3169593  
    7474    }
    7575
     76    /**
     77     * An order update is always processed backend so it will not interrupt customer process.
     78     * Due to that, we should always process immediately and not on a cron to keep data from going stale
     79     * or from losing the data due to quick status changes.
     80     * FYI New orders do not go through this process.
     81     *
     82     * @param string|int $order_id The order id as passed from the hook.
     83     */
    7684    public function execute_order_updated( $order_id ) {
    7785        $logger = new Logger();
    7886
    7987        if ( isset( $order_id ) && ! empty( $order_id ) ) {
    80             $logger->debug(
     88            $logger->debug_excess(
    8189                'Order update triggered',
    8290                [
     
    9098            if ( 'shop_subscription' === $post_type ) {
    9199                $wc_subscription = $this->get_wc_subscription( $order_id );
    92                 do_action( 'activecampaign_for_woocommerce_route_order_update_to_subscription', [ $wc_subscription ] );
     100
     101                if ( $this->check_update_validity( $wc_subscription ) ) {
     102                    do_action( 'activecampaign_for_woocommerce_route_order_update_to_subscription', [ $wc_subscription ] );
     103                }
     104
    93105                return;
    94106            }
     
    99111            }
    100112
    101             set_transient( 'acforwc_order_updated_hook', wp_date( DATE_ATOM ), 604800 );
    102 
    103113            $wc_order = $this->get_wc_order( $order_id );
    104114
    105115            // Check if order is valid
    106             if ( self::validate_object( $wc_order, 'get_data' ) ) {
    107                 // This will sync it immediately but also blindly
    108                 if ( ! wp_get_scheduled_event( 'activecampaign_for_woocommerce_admin_sync_single_order_active', [ 'wc_order_id' => $order_id ] ) ) {
     116            if ( self::validate_object( $wc_order, 'get_data' ) && $this->check_update_validity( $wc_order ) ) {
     117                set_transient( 'acforwc_order_updated_hook', wp_date( DATE_ATOM ), 604800 );
     118
     119                if ( ! wp_get_scheduled_event(
     120                    'activecampaign_for_woocommerce_admin_sync_single_order_active',
     121                    [
     122                        'wc_order_id' => $order_id,
     123                        'status'      => $wc_order->get_status(),
     124                    ]
     125                ) &&
     126                    ! wp_get_scheduled_event(
     127                        'activecampaign_for_woocommerce_admin_sync_single_order_status',
     128                        [
     129                            'wc_order_id' => $order_id,
     130                            'status'      => $wc_order->get_status(),
     131                        ]
     132                    ) ) {
    109133                    wp_schedule_single_event(
    110                         time() + 30,
     134                        time() + 10,
    111135                        'activecampaign_for_woocommerce_admin_sync_single_order_active',
    112136                        [
    113137                            'wc_order_id' => $order_id,
     138                            'status'      => $wc_order->get_status(),
    114139                        ]
    115140                    );
     
    127152
    128153    /**
    129      * @param object   $stripe_response The stripe response.
    130      * @param WC_Order $order The order.
    131      */
    132     public function execute_order_updated_stripe( $stripe_response, $order ) {
    133         $logger = new Logger();
    134 
    135         $order_id = null;
     154     * Order status updates are processed through this function.
     155     *
     156     * @param int|string $order_id The order id.
     157     * @param string     $from_status The status the order changed from.
     158     * @param string     $to_status The status the order is changing to.
     159     */
     160    public function execute_order_status_changed( $order_id, $from_status, $to_status ) {
     161        $logger = new Logger();
     162
     163        if ( isset( $order_id ) && ! empty( $order_id ) ) {
     164            $logger->debug_excess(
     165                'Order status update triggered',
     166                [
     167                    'order'        => $order_id,
     168                    'order_status' => $from_status,
     169                    'new_status'   => $to_status,
     170                ]
     171            );
     172
     173            $post_type = get_post_type( $order_id );
     174
     175            // If it's a subscription, route through subscription update.
     176            if ( 'shop_subscription' === $post_type ) {
     177                $wc_subscription = $this->get_wc_subscription( $order_id );
     178
     179                do_action( 'activecampaign_for_woocommerce_route_order_update_to_subscription', [ $wc_subscription ] );
     180                return;
     181            }
     182
     183            // If it's not an order do nothing, this could be anything
     184            if ( 'shop_order' !== $post_type ) {
     185                $logger->debug_excess(
     186                    'Order status update was triggered but the post is not a shop order type.',
     187                    [
     188                        'order_id'   => $order_id,
     189                        'post_type'  => $post_type,
     190                        'new_status' => $to_status,
     191                    ]
     192                );
     193
     194                return;
     195            }
     196
     197            $wc_order = $this->get_wc_order( $order_id );
     198
     199            // Check if order is valid
     200            if ( self::validate_object( $wc_order, 'get_data' ) ) {
     201                set_transient( 'acforwc_order_updated_hook', wp_date( DATE_ATOM ), 604800 );
     202
     203                if ( ! wp_get_scheduled_event(
     204                    'activecampaign_for_woocommerce_admin_sync_single_order_status',
     205                    [
     206                        'wc_order_id' => $order_id,
     207                        'status'      => $to_status,
     208                    ]
     209                ) ) {
     210                    wp_schedule_single_event(
     211                        time() + 10,
     212                        'activecampaign_for_woocommerce_admin_sync_single_order_status',
     213                        [
     214                            'wc_order_id' => $order_id,
     215                            'status'      => $to_status,
     216                        ]
     217                    );
     218                }
     219            }
     220        } else {
     221            $logger->warning(
     222                'The updated order does not appear to be valid for sync to AC.',
     223                [
     224                    'order_id' => $order_id,
     225                ]
     226            );
     227        }
     228    }
     229
     230    public function woocommerce_order_edit_status( $order_id, $new_status ) {
     231        $wc_order = $this->get_wc_order( $order_id );
     232
     233        if ( self::validate_object( $wc_order, 'get_data' ) ) {
     234            set_transient( 'acforwc_order_updated_hook', wp_date( DATE_ATOM ), 604800 );
     235
     236            if ( ! wp_get_scheduled_event(
     237                'activecampaign_for_woocommerce_admin_sync_single_order_status',
     238                [
     239                    'wc_order_id' => $order_id,
     240                    'event_type'  => 'status_' . $new_status,
     241                ]
     242            ) ) {
     243                wp_schedule_single_event(
     244                    time() + 10,
     245                    'activecampaign_for_woocommerce_admin_sync_single_order_status',
     246                    [
     247                        'wc_order_id' => $order_id,
     248                        'status'      => $new_status,
     249                    ]
     250                );
     251            }
     252        }
     253    }
     254
     255    /**
     256     * The process used for an order refund. Technically this is the same as an order update.
     257     * We pass this through a different function to note it in debug until we have relevant handling.
     258     *
     259     * @param string|int $order_id The order id.
     260     * @param string|int $refund_id Refund id is passed and required but not used here.
     261     */
     262    public function execute_order_updated_refund( $order_id, $refund_id ) {
     263        $logger = new Logger();
    136264
    137265        try {
    138             $order_id = $order->get_id();
    139 
    140             if ( isset( $order_id ) && ! empty( $order_id ) ) {
    141                 $logger->debug(
    142                     'Stripe verified order triggered',
    143                     [
    144                         'order_id' => $order_id,
    145                     ]
    146                 );
    147 
    148                 $this->execute_order_updated( $order_id );
     266            $logger->debug_excess(
     267                'Refund order update triggered',
     268                [
     269                    'order_id' => $order_id,
     270                ]
     271            );
     272
     273            $this->execute_order_updated( $order_id );
     274        } catch ( Throwable $t ) {
     275            $logger->warning(
     276                'There was an issue updating the order from a refund trigger.',
     277                [
     278                    'order_id' => $order_id,
     279                    'message'  => $t->getMessage(),
     280                ]
     281            );
     282        }
     283    }
     284
     285    /**
     286     * Stripe documentation is unhelpful but we need to process its updates. We do not use the response but it must be
     287     * included in the function args.
     288     *
     289     * @param object|string   $sripe_response Stripe response, unused.
     290     * @param string|WC_Order $order Could be order object or order id, stripe does not say.
     291     */
     292    public function execute_order_updated_stripe( $sripe_response, $order ) {
     293        $logger = new Logger();
     294
     295        try {
     296            if ( isset( $sripe_response ) && isset( $order ) ) {
     297                $wc_order = $this->get_wc_order( $order ); // Be sure we have the WC Order
     298                $order_id = $wc_order->get_id();
     299                if ( isset( $order_id ) && ! empty( $order_id ) ) {
     300                    $logger->debug_excess(
     301                        'Stripe verified order update triggered',
     302                        [
     303                            'order_id' => $order_id,
     304                        ]
     305                    );
     306
     307                    $this->execute_order_updated( $order_id );
     308                }
    149309            }
    150310        } catch ( Throwable $t ) {
     
    152312                'There was an issue updating the order from stripe.',
    153313                [
    154                     'order_id' => $order_id,
    155                     'message'  => $t->getMessage(),
     314                    'order'   => $order,
     315                    'message' => $t->getMessage(),
    156316                ]
    157317            );
     
    236396        }
    237397    }
     398
     399    /**
     400     * Check to make sure we are not double syncing the same data.
     401     *
     402     * @param WC_Subscription|WC_Order $wc_order The order or subscription. Both carry these functions.
     403     *
     404     * @return bool
     405     */
     406    private function check_update_validity( $wc_order ) {
     407        $ac_datahash      = $wc_order->get_meta( 'ac_datahash' );
     408        $current_datahash = md5( json_encode( $wc_order->get_data() ) );
     409        $last_synced      = $wc_order->get_meta( 'ac_order_last_synced_time' );
     410        $last_status      = $wc_order->get_meta( 'ac_last_synced_status' );
     411
     412        if (
     413        time() - $last_synced > 120 &&
     414        isset( $last_status ) &&
     415        $last_status === $wc_order->get_status() &&
     416        $ac_datahash === $current_datahash
     417        ) {
     418            return false;
     419        }
     420
     421        return true;
     422    }
    238423}
  • activecampaign-for-woocommerce/trunk/includes/products/class-activecampaign-for-woocommerce-product-sync-job.php

    r3049374 r3169593  
    2828 */
    2929class Activecampaign_For_Woocommerce_Product_Sync_Job implements Executable {
    30     use Activecampaign_For_Woocommerce_Data_Validation;
     30    use Activecampaign_For_Woocommerce_Data_Validation,
     31        Activecampaign_For_Woocommerce_Arg_Data_Gathering;
    3132    /**
    3233     * The custom ActiveCampaign logger
     
    232233
    233234    /**
    234      * Gets the product IDs in the format we need.
    235      *
    236      * @param int  $limit The limit.
    237      * @param int  $offset The offset.
    238      * @param bool $return_id_only Marker for return IDs only.
    239      *
    240      * @return array|stdClass
    241      */
    242     private function get_products_by_offset( $limit, $offset, $return_id_only ) {
    243         // types available 'external', 'grouped', 'simple', 'variable'
    244         // Do not include groups for now.
    245         try {
    246             $safe_product_types = $this->get_cofe_safe_product_types();
    247 
    248             $data = [
    249                 'limit'   => (int) $limit,
    250                 'offset'  => (int) $offset,
    251                 'type'    => $safe_product_types,
    252                 'orderby' => 'none',
    253                 'order'   => 'ASC',
    254             ];
    255 
    256             if ( $return_id_only ) {
    257                 $data['return'] = 'ids';
    258             }
    259 
    260             $this->logger->debug(
    261                 'Getting products by offset',
    262                 [
    263                     'producttypes'   => $safe_product_types,
    264                     'data'           => $data,
    265                     'return_id_only' => $return_id_only,
    266                 ]
    267             );
    268 
    269             $products = wc_get_products( $data );
    270 
    271             return $products;
    272         } catch ( Throwable $t ) {
    273             $this->logger->warning(
    274                 'There was an issue getting products for the product sync',
    275                 [
    276                     'message'        => $t->getMessage(),
    277                     'request data'   => $data,
    278                     'return_id_only' => $return_id_only,
    279                 ]
    280             );
    281         }
    282         return null;
    283     }
    284 
    285     /**
    286235     * Build the product sync schedules based on number of products.
    287236     *
     
    625574        global $activecampaign_for_woocommerce_product_sync_status;
    626575        try {
    627             $p_data = Ecom_Cofe_Product::product_array_for_cofe( $product, $connection_id, $parent );
    628             if ( ! is_null( $p_data ) ) {
    629                 $product_data[] = $p_data;
     576            if ( $product->is_type( 'grouped' ) || $product->is_type( 'draft' ) ) {
     577                $this->add_failed_product_to_status( $status, $product );
     578            } else {
     579                $p_data = Ecom_Cofe_Product::product_array_for_cofe( $product, $connection_id, $parent );
     580                if ( ! is_null( $p_data ) ) {
     581                    $product_data[] = $p_data;
     582                }
    630583            }
    631584        } catch ( Throwable $t ) {
     
    692645    private function add_failed_product_to_status( Sync_Status $status, ?WC_Product $product = null ) {
    693646        if ( $product && self::validate_object( $product, 'get_id' ) && ! empty( $product->get_id() ) ) {
    694             $status->failed_order_id_array[] = $product->get_id();
     647            $status->failed_id_array[] = $product->get_id();
    695648        } else {
    696             $status->failed_order_id_array[] = 'Unknown WC Product';
     649            $status->failed_id_array[] = 'Unknown WC Product';
    697650        }
    698651    }
     
    789742    }
    790743
    791     /**
    792      * Gets the safe COFE product types. This is a blacklist to remove any types that cause duplicates, conflicts, or issues with sync.
    793      *
    794      * @return int[]|string[]
    795      */
    796     private function get_cofe_safe_product_types() {
    797         $product_types = wc_get_product_types();
    798 
    799         // Blacklist certain types that cause conflicts & duplicates
    800         if ( isset( $product_types['grouped'] ) ) {
    801             unset( $product_types['grouped'] );
    802         }
    803 
    804         if ( isset( $product_types['draft'] ) ) {
    805             unset( $product_types['draft'] );
    806         }
    807 
    808         // WC returns array as type_name: type readable so return only the keys
    809         return array_keys( $product_types );
    810     }
    811 
    812744    public function execute_product_deleted( $product_id ) {
    813745        $logger = new Logger();
  • activecampaign-for-woocommerce/trunk/includes/products/class-activecampaign-for-woocommerce-sync-status.php

    r3068573 r3169593  
    5151            $data['last_update'],
    5252            $data['end_time'],
    53             $data['failed_order_id_array'],
     53            $data['failed_id_array'],
    5454            $data['is_running'],
    5555            $data['status_name']
  • activecampaign-for-woocommerce/trunk/includes/subscriptions/class-activecampaign-for-woocommerce-new-subscription-sync-job.php

    r3100804 r3169593  
    150150                    $this->mark_order_as_incompatible( $wc_order_id );
    151151                    return false;
    152                 }
    153 
    154                 $ac_customer_id = $this->get_ac_customer_id( $wc_subscription->get_billing_email() );
    155                 $ac_order       = $this->single_sync_subscription_cofe_data( $wc_subscription, false, $ac_customer_id );
    156 
    157                 if ( ! isset( $ac_order ) || ! $ac_order ) {
    158                     $this->logger->warning(
    159                         'The order may have failed to sync to cofe',
    160                         [
    161                             'order_id'  => $wc_order_id,
    162                             'sync_data' => $ac_order,
    163                             'ac_code'   => 'SSJ_158',
    164                         ]
    165                     );
    166 
    167                     $this->mark_subscription_as_failed( $wc_order_id );
    168152                } else {
    169                     $ac_id = null;
    170 
    171                     if ( self::validate_object( $ac_order, 'get_id' ) ) {
    172                         $ac_id = $ac_order->get_id();
     153
     154                    $ac_customer_id = $this->get_ac_customer_id( $wc_subscription->get_billing_email() );
     155                    $ac_order       = $this->single_sync_subscription_cofe_data( $wc_subscription, false, $ac_customer_id );
     156
     157                    if ( ! isset( $ac_order ) || ! $ac_order ) {
     158                        $this->logger->warning(
     159                            'The order may have failed to sync to cofe',
     160                            [
     161                                'order_id'  => $wc_order_id,
     162                                'sync_data' => $ac_order,
     163                                'ac_code'   => 'SSJ_158',
     164                            ]
     165                        );
     166
     167                        $this->mark_subscription_as_failed( $wc_order_id );
     168                    } else {
     169                        $ac_id = null;
     170
     171                        if ( self::validate_object( $ac_order, 'get_id' ) ) {
     172                            $ac_id = $ac_order->get_id();
     173                        }
     174
     175                        $this->add_meta_to_subscription( $wc_subscription );
     176                        $this->add_update_notes( $wc_order_id, $ac_id, $wc_subscription->get_status() );
     177                        $this->mark_single_order_synced( $wc_order_id );
     178                        $this->update_last_subscription_sync();
    173179                    }
    174 
    175                     $this->add_update_notes( $wc_order_id, $ac_id, $wc_subscription->get_status() );
    176                     $this->mark_single_order_synced( $wc_order_id );
    177                     $this->update_last_subscription_sync();
    178180                }
    179181            }
     
    602604        }
    603605    }
     606
     607        /**
     608         * @param WC_Subscription $wc_subscription The WooCommerce order object.
     609         */
     610    private function add_meta_to_subscription( $wc_subscription ) {
     611        // save the status so update checks do not sync the same data
     612        $wc_subscription->add_meta_data( 'ac_last_synced_status', $wc_subscription->get_status(), true );
     613
     614        $last_sync_time = $wc_subscription->get_meta( 'ac_order_last_synced_time' );
     615        $ac_datahash    = $wc_subscription->get_meta( 'ac_datahash' );
     616
     617        if ( ! empty( $last_sync_time ) ) {
     618            $wc_subscription->update_meta_data( 'ac_order_last_synced_time', time() );
     619        } else {
     620            $wc_subscription->add_meta_data( 'ac_order_last_synced_time', time(), true );
     621        }
     622
     623        if ( ! empty( $ac_datahash ) ) {
     624            $wc_subscription->update_meta_data( 'ac_datahash', md5( json_encode( $wc_subscription->get_data() ) ) );
     625        } else {
     626            $wc_subscription->add_meta_data( 'ac_datahash', md5( json_encode( $wc_subscription->get_data() ) ), true );
     627        }
     628
     629        $wc_subscription->save_meta_data();
     630    }
    604631}
  • activecampaign-for-woocommerce/trunk/includes/traits/trait-activecampaign-for-woocommerce-arg-data-gathering.php

    r3089566 r3169593  
    108108
    109109    }
     110
     111    /**
     112     * Gets the product IDs in the format we need.
     113     *
     114     * @param int  $limit The limit.
     115     * @param int  $offset The offset.
     116     * @param bool $return_id_only Marker for return IDs only.
     117     *
     118     * @return array|stdClass
     119     */
     120    public static function get_products_by_offset( $limit, $offset, $return_id_only ) {
     121        // types standard available 'external', 'grouped', 'simple', 'variable'
     122        // Do not include groups for now.
     123        $logger = new Logger();
     124        try {
     125            $safe_product_types = self::get_cofe_safe_product_types(); // This may be causing an issue with some 3rd party plugins due to custom product types.
     126
     127            $data = [
     128                'limit'   => (int) $limit,
     129                'offset'  => (int) $offset,
     130                'orderby' => 'ID',
     131                'status'  => 'publish',
     132                'order'   => 'ASC',
     133            ];
     134
     135            if ( isset( $safe_product_types ) && ! empty( $safe_product_types ) ) {
     136                $data['type'] = $safe_product_types;
     137            }
     138
     139            if ( $return_id_only ) {
     140                $data['return'] = 'ids';
     141            }
     142
     143            $logger->debug(
     144                'Getting products by offset',
     145                [
     146                    'producttypes'   => $safe_product_types,
     147                    'data'           => $data,
     148                    'return_id_only' => $return_id_only,
     149                ]
     150            );
     151
     152            $products = wc_get_products( $data );
     153
     154            return $products;
     155        } catch ( Throwable $t ) {
     156            $logger = new Logger();
     157            $logger->warning(
     158                'There was an issue getting products for the product sync',
     159                [
     160                    'message'        => $t->getMessage(),
     161                    'return_id_only' => $return_id_only,
     162                ]
     163            );
     164        }
     165        return null;
     166    }
     167
     168
     169
     170    public static function get_cofe_safe_product_types() {
     171        $product_types = wc_get_product_types();
     172
     173        // Blacklist certain types that cause conflicts & duplicates
     174        if ( isset( $product_types['grouped'] ) ) {
     175            unset( $product_types['grouped'] ); // Grouped products are bundles of existing single products. These cause duplicate records.
     176        }
     177
     178        if ( isset( $product_types['draft'] ) ) {
     179            unset( $product_types['draft'] ); // Never sync drafts
     180        }
     181
     182        // WC returns array as type_name: type readable so return only the keys
     183        return array_keys( $product_types );
     184    }
     185
    110186}
Note: See TracChangeset for help on using the changeset viewer.