Plugin Directory

Changeset 3225869


Ignore:
Timestamp:
01/21/2025 01:52:35 AM (14 months ago)
Author:
squarewoosync
Message:

fix auto sync of orders for non squaresync gateways

Location:
squarewoosync
Files:
92 added
6 edited

Legend:

Unmodified
Added
Removed
  • squarewoosync/trunk/includes/REST/OrdersController.php

    r3198659 r3225869  
    100100      }
    101101
     102
     103      $payment_gateways = WC()->payment_gateways->payment_gateways();
     104
    102105      $settings = get_option('square-woo-sync_settings', []);
    103       if (isset($settings['orders']['transactions']) && $settings['orders']['transactions'] === true) {
     106      if ((isset($settings['orders']['transactions']) && $settings['orders']['transactions'] === true) || (isset($payment_gateways['squaresync_credit']) && $payment_gateways['squaresync_credit']->enabled === 'yes')) {
    104107        $payResponse = $this->payForOrder($response['data']['order'], $square);
    105108
     
    280283    $customer_note = $order->get_customer_note();
    281284    if (!empty($customer_note)) {
    282         $order_data['order']['note'] = mb_substr($customer_note, 0, 500); // Limit to 500 characters
     285      $order_data['order']['note'] = mb_substr($customer_note, 0, 500); // Limit to 500 characters
    283286    }
    284287
  • squarewoosync/trunk/includes/Woo/SyncProduct.php

    r3145648 r3225869  
    66use Pixeldev\SquareWooSync\REST\OrdersController;
    77use Pixeldev\SquareWooSync\Square\SquareHelper;
    8 use Pixeldev\SquareWooSync\Woo\WooImport;
    98
    109if (!defined('ABSPATH')) {
    11     exit; // Exit if accessed directly
     10  exit; // Exit if accessed directly
    1211}
    1312
     
    1716class SyncProduct
    1817{
    19     private $customer_update_in_progress = false;
    20     private $inventory_update_in_progress = false;
    21     /**
    22      * Constructor.
    23      */
    24     public function __construct()
    25     {
    26         add_action('init', array($this, 'init_woo_product'));
     18  private $inventory_update_in_progress = false;
     19  /**
     20   * Constructor.
     21   */
     22  public function __construct()
     23  {
     24    add_action('init', array($this, 'init_woo_product'));
     25  }
     26
     27  /**
     28   * Initialize WooCommerce Product hooks.
     29   */
     30  public function init_woo_product()
     31  {
     32    if (class_exists('WooCommerce')) {
     33   
     34      add_action('sws_sync_order_after_product_sold_event', [$this, 'create_order_in_background']);
     35      add_action('woocommerce_order_status_changed', array($this, 'create_square_order_after_woo_order'), 20, 4);
     36
     37      add_action('woocommerce_checkout_create_order', array($this, 'set_order_meta'), 10, 2);
     38    }
     39  }
     40
     41  public function set_order_meta($order, $data)
     42  {
     43    // Initialize an array to store all unique locations
     44    $order_locations = [];
     45
     46    // Loop through each line item in the order
     47    foreach ($order->get_items() as $item_id => $item) {
     48      // Get the product ID
     49      $product_id = $item->get_product_id();
     50
     51      // Retrieve the 'square_locations' meta data for the product
     52      $locations = get_post_meta($product_id, 'square_locations', true);
     53
     54      if (!empty($locations)) {
     55        // Split the comma-separated locations into an array
     56        $locations_array = explode(',', $locations);
     57
     58        // Merge with the existing order locations, ensuring uniqueness
     59        $order_locations = array_unique(array_merge($order_locations, $locations_array));
     60      }
    2761    }
    2862
    29     /**
    30      * Initialize WooCommerce Product hooks.
    31      */
    32     public function init_woo_product()
    33     {
    34         if (class_exists('WooCommerce')) {
    35             add_action('add_meta_boxes', array($this, 'add_sync_meta_box'));
    36             add_action('admin_post_sync_to_square', array($this, 'handle_sync_to_square'));
    37             add_action('admin_post_export_to_square', array($this, 'handle_export_to_square'));
    38             add_action('admin_footer', array($this, 'add_ajax_script'));
    39             add_action('wp_ajax_sync_to_square', array($this, 'handle_ajax_sync_to_square'));
    40             add_action('wp_ajax_export_to_square', array($this, 'handle_ajax_export_to_square'));
    41             add_action('sws_sync_inventory_after_product_sold_event', [$this, 'sync_inventory_after_product_sold']);
    42             add_action('sws_sync_order_after_product_sold_event', [$this, 'create_order_in_background']);
     63    if (!empty($order_locations)) {
     64      // Save the combined, unique locations as a comma-separated string
     65      $order->update_meta_data('square_locations', implode(',', $order_locations));
     66    }
     67  }
     68
     69  public function create_order_in_background($order_id)
     70  {
     71    $order = wc_get_order($order_id);
     72    $uniqueProcessId = wp_generate_uuid4();
    4373
    4474
    45             add_action('woocommerce_order_status_changed', array($this, 'create_square_order_after_woo_order'), 20, 4);
     75    Logger::log('info', 'Initiating order sync to Square for order #' . $order_id, array('process_id' => $uniqueProcessId));
     76
     77    $ordersController = new OrdersController();
     78    $square = new SquareHelper();
     79
     80    try {
     81      // Retrieve or create Square customer ID
     82      $square_customer_id = $ordersController->getOrCreateSquareCustomer($order, $square);
    4683
    4784
    48             // Export product to Square on new Woo Product
    49             add_action('transition_post_status',  array($this, 'export_to_square'), 10, 31);
     85      if (isset($square_customer_id['error'])) {
     86        Logger::log('error', 'Square Orders error: ' .  $square_customer_id['error'], array('parent_id' => $uniqueProcessId));
     87      }
    5088
    51             // Update square customer
    52             add_action('profile_update',  array($this, 'update_square_customer'), 10, 1);
    53             add_action('woocommerce_update_customer',  array($this, 'update_square_customer'), 10, 1);
    54         }
    55     }
     89      // Prepare order data for Square
     90      $order_data = $ordersController->prepareSquareOrderData($order, $square_customer_id);
     91      $response = $ordersController->createOrderInSquare($order_data, $square);
    5692
    57     private function get_square_customer_payload($customer, $settings)
    58     {
    59         $first_name = sanitize_text_field($customer->get_first_name());
    60         $last_name = sanitize_text_field($customer->get_last_name());
     93      // Check for errors in the response
     94      if (isset($response['error'])) {
     95        Logger::log('error', 'Square Orders API error: ' .  $response['error'], array('parent_id' => $uniqueProcessId));
     96      }
    6197
    62         // Generate a unique idempotency key
    63         $idempotency_key = uniqid('square_', true);
     98      $payReponse = $ordersController->payForOrder($response['data']['order'], $square);
    6499
    65         $payload = [
    66             'idempotency_key' => $idempotency_key,
    67         ];
     100      if (isset($payReponse['error'])) {
     101        Logger::log('error', 'Square Payment API error: ' . $payReponse['error'], array('parent_id' => $uniqueProcessId));
     102      }
    68103
    69         if (isset($settings['first_name']) && $settings['first_name'] === true && !empty($first_name)) {
    70             $payload['given_name'] = $first_name;
     104
     105      if (isset($response['data']['order']['id']) && isset($payReponse['data']['payment']['id'])) {
     106        $square_data =  ['order' => $response, 'payment' => $payReponse];
     107
     108        // Save Square order ID and payment ID to WooCommerce order meta
     109        $order->update_meta_data('square_data', wp_json_encode($square_data));
     110        if (isset($response['data']['order']['id'])) {
     111          $order->update_meta_data('square_order_id',  $response['data']['order']['id']);
    71112        }
    72113
    73         if (isset($settings['last_name']) && $settings['last_name'] === true && !empty($last_name)) {
    74             $payload['family_name'] = $last_name;
    75         }
     114        // Save changes to the order
     115        $order->save();
     116      }
    76117
    77         if (isset($settings['phone']) && $settings['phone'] === true && !empty($customer->get_billing_phone())) {
    78             $payload['phone_number'] = $customer->get_billing_phone();
    79         }
     118      // if (!empty($current_settings) || !empty($current_settings['loyalty']) || $current_settings['loyalty']['enabled'] === true) {
     119      //     $loyalty = new LoyaltyProgram();
     120      //     $loyalty->accumulate_loylty_points($response['data']['order']['id'], $current_settings['loyalty']['program_id'], $square_customer_id);
     121      // }
    80122
    81         if (isset($settings['address']) && $settings['address'] === true) {
    82             $address = [];
    83             if (!empty($customer->get_billing_address_1())) {
    84                 $address['address_line_1'] = $customer->get_billing_address_1();
    85             }
    86             if (!empty($customer->get_billing_address_2())) {
    87                 $address['address_line_2'] = $customer->get_billing_address_2();
    88             }
    89             if (!empty($customer->get_billing_city())) {
    90                 $address['locality'] = $customer->get_billing_city();
    91             }
    92             if (!empty($customer->get_billing_state())) {
    93                 $address['administrative_district_level_1'] = $customer->get_billing_state();
    94             }
    95             if (!empty($customer->get_billing_postcode())) {
    96                 $address['postal_code'] = $customer->get_billing_postcode();
    97             }
    98             if (!empty($customer->get_billing_country())) {
    99                 $address['country'] = $customer->get_billing_country();
    100             }
    101             if (!empty($address)) {
    102                 $payload['address'] = $address;
    103             }
    104         }
    105 
    106         return $payload;
     123      Logger::log('success', 'Order and Transaction created in Square, receipt: #' . $payReponse['data']['payment']['receipt_number'], array('parent_id' => $uniqueProcessId));
     124    } catch (\Exception $e) {
     125      if ($e->getMessage() == 'Square location not set') {
     126        Logger::log('error', 'Square location not set: ' . $e->getMessage(), array('parent_id' => $uniqueProcessId));
     127      }
     128      Logger::log('error', 'Failed to create order: ' . $e->getMessage(), array('parent_id' => $uniqueProcessId));
    107129    }
    108 
    109     function update_square_customer_roles($customer_id, $square_customer_id)
    110     {
    111         if ($this->customer_update_in_progress) {
    112             return;
    113         }
    114 
    115         $this->customer_update_in_progress = true;
    116 
    117         $user = get_user_by('id', $customer_id);
    118         if ($user) {
    119             $current_roles = $user->roles;
    120             $group_ids_to_add = [];
    121             $group_ids_to_remove = [];
    122 
    123             // Get role mappings from settings
    124             $settings = get_option('square-woo-sync_settings', []);
    125             $role_mappings = $settings['customers']['roleMappings'] ?? [];
    126 
    127             // Determine group IDs to add based on user's roles
    128             foreach ($current_roles as $current_role) {
    129                 foreach ($role_mappings as $role => $mapping) {
    130                     if ($current_role === $role && isset($mapping['groupId'])) {
    131                         $group_ids_to_add[] = sanitize_text_field($mapping['groupId']);
    132                     }
    133                 }
    134             }
    135 
    136             // Ensure group IDs are unique and not empty
    137             $group_ids_to_add = array_filter(array_unique($group_ids_to_add), function ($value) {
    138                 return !empty($value) && $value !== 'N/A';
    139             });
    140 
    141             // Determine group IDs to remove
    142             foreach ($role_mappings as $role => $mapping) {
    143                 if (!in_array($role, $current_roles) && isset($mapping['groupId'])) {
    144                     $group_ids_to_remove[] = sanitize_text_field($mapping['groupId']);
    145                 }
    146             }
    147 
    148             $squareHelper = new SquareHelper();
    149 
    150             // Remove customer from groups
    151             foreach ($group_ids_to_remove as $group_id) {
    152                 $remove_response = $squareHelper->square_api_request('/customers/' . $square_customer_id . '/groups/' . $group_id, 'DELETE');
    153                 if (!$remove_response['success']) {
    154                     error_log('Failed to remove Square customer from group: ' . $group_id);
    155                 }
    156             }
    157 
    158             // Add customer to groups
    159             foreach ($group_ids_to_add as $group_id) {
    160                 $group_response = $squareHelper->square_api_request('/customers/' . $square_customer_id . '/groups/' . $group_id, 'PUT');
    161                 if (!$group_response['success']) {
    162                     error_log('Failed to add Square customer to group: ' . $group_id);
    163                 }
    164             }
    165         }
    166 
    167         $this->customer_update_in_progress = false;
    168     }
    169 
    170     public function update_square_customer($customer_id)
    171     {
    172         $uniqueProcessId = wp_generate_uuid4();
    173         $logger = new Logger();
    174 
    175         // Get the customer data
    176         $customer = new \WC_Customer($customer_id);
    177 
    178         // Get settings to check if fields are active
    179         $all_settings = get_option('square-woo-sync_settings', []);
    180         $settings = $all_settings['customers']['auto']['wooSquare'] ?? [];
     130  }
    181131
    182132
    183         if (!$settings['is_active']) {
    184             return;
    185         }
     133  /**
     134   * Handle actions on order status change.
     135   *
     136   * @param int    $order_id The order ID.
     137   * @param string $old_status The old order status.
     138   * @param string $new_status The new order status.
     139   * @param  $order The order object.
     140   */
     141  public function create_square_order_after_woo_order($order_id, $old_status = '', $new_status = '', $order = null)
     142  {
     143    // Check if the event is already scheduled
     144    $timestamp = wp_next_scheduled('sws_sync_inventory_after_product_sold_event', [$order_id]);
    186145
    187         // Check if the update is coming from WooCommerce
    188         if (get_user_meta($customer_id, '_update_source', true) === 'square') {
    189             // Reset the source to avoid loops
    190             update_user_meta($customer_id, '_update_source', '');
    191             return;
    192         }
    193 
    194         // Add a source identifier
    195         update_user_meta($customer_id, '_update_source', 'woo');
    196 
    197         // Prepare customer data for update
    198         $square_customer_id = get_user_meta($customer_id, 'square_customer_id', true);
    199         if ($square_customer_id) {
    200             $customer_data = $this->get_square_customer_payload($customer, $settings);
    201 
    202             SquareHelper::queue_request('/customers/' . $square_customer_id, 'PUT', $customer_data, null, function ($response) use ($uniqueProcessId, $logger, $customer_id, $square_customer_id,  $customer) {
    203                 if (!$response['success']) {
    204                     error_log('Failed to update Square customer: ' . json_encode($response['data']));
    205 
    206                     $logger->log('error', 'Failed to update Square customer: ' . json_encode($response['data']), array('error_message' => json_encode($response['data']), 'process_id' => $uniqueProcessId));
    207                 } else {
    208                     if (isset($settings['role']) && $settings['role'] === true) {
    209                         $this->update_square_customer_roles($customer_id, $square_customer_id);
    210                     }
    211                     $logger->log('success', 'Customer ' . $customer->get_email() . ' has been successfully updated in Square', array('process_id' => $uniqueProcessId));
    212                 }
    213             });
    214         }
    215     }
    216 
    217     /**
    218      * Adds a meta box for syncing with Square.
    219      */
    220     public function add_sync_meta_box()
    221     {
    222         add_meta_box(
    223             'sws_sync_square',
    224             'Sync with Square',
    225             array($this, 'sync_meta_box_html'),
    226             'product',
    227             'side',
    228             'high'
    229         );
    230     }
    231 
    232     /**
    233      * Adds JavaScript for AJAX synchronization.
    234      */
    235     public function add_ajax_script()
    236     {
    237         $screen = get_current_screen();
    238         if ('product' !== $screen->id) {
    239             return;
    240         }
    241 
    242         $js_file_url =  plugins_url('', SQUAREWOOSYNC_FILE) . '/assets/js/sync-metabox.js';
    243 
    244         wp_enqueue_script('sws-custom-script', $js_file_url, array('jquery'), '1.0', true);
    245         wp_localize_script('sws-custom-script', 'swsAjax', array(
    246             'ajaxurl' => admin_url('admin-ajax.php'),
    247             'nonce'   => wp_create_nonce('sws_ajax_nonce'),
    248         ));
    249     }
    250 
    251 
    252     public function sync_inventory_after_product_sold($order_id)
    253     {
    254         $order = wc_get_order($order_id);
    255         // Check if $order is a valid WC_Order object and return early if not
    256         if ($this->inventory_update_in_progress) {
    257             return;
    258         }
    259 
    260         // Get the current settings once, and return early if the sync isn't enabled or configured correctly
    261         $current_settings = get_option('square-woo-sync_settings', []);
    262         if (empty($current_settings) || !$current_settings['wooAuto']['isActive'] || !$current_settings['wooAuto']['stock']) {
    263             return;
    264         }
    265 
    266         $this->inventory_update_in_progress = true;
    267 
    268         // Initialize variables
    269         $uniqueProcessId = wp_generate_uuid4();
    270         $hasSquareLinkedProduct = false;
    271         $logger = null; // Defer logger initialization
    272         $square_product_ids = []; // To store all square_product_ids with corresponding WooCommerce product IDs
    273 
    274         // Loop through order items
    275         foreach ($order->get_items() as $item) {
    276             $product = $item->get_product();
    277             if (!$product || !$product->managing_stock()) {
    278                 continue; // Skip if the product is not managing stock
    279             }
    280 
    281             // Check if the product is a variation
    282             if ($product->is_type('variation')) {
    283                 $parent_product = wc_get_product($product->get_parent_id());
    284                 $square_product_id = $parent_product ? $parent_product->get_meta('square_product_id') : null;
    285             } else {
    286                 $square_product_id = $product->get_meta('square_product_id');
    287             }
    288 
    289             if (!$square_product_id) {
    290                 continue; // Skip if the product is not linked to Square
    291             }
    292 
    293             // Add the square_product_id and WooCommerce product ID to the list for syncing
    294             $square_product_ids[] = [
    295                 'square_product_id' => $square_product_id,
    296                 'woo_product_id' => $product->get_id()
    297             ];
    298 
    299             // At least one product is linked to Square
    300             $hasSquareLinkedProduct = true;
    301         }
    302 
    303         // Log the parent entry only if at least one product is linked to Square
    304         if ($hasSquareLinkedProduct) {
    305             if (!$logger) {
    306                 $logger = new Logger();
    307             }
    308             $logger->log('info', 'Initiating inventory sync from WooCommerce to Square', ['process_id' => $uniqueProcessId]);
    309         }
    310 
    311         // Queue request to retrieve Square catalog objects
    312         SquareHelper::queue_request('/catalog/batch-retrieve', 'POST', [
    313             'object_ids' => array_column($square_product_ids, 'square_product_id')
    314         ], null, function ($response) use ($uniqueProcessId, $logger, $square_product_ids, $current_settings) {
    315             if (!$logger) {
    316                 $logger = new Logger();
    317             }
    318 
    319             if (!$response['success']) {
    320                 error_log('Failed to update Square inventory: ' . json_encode($response['data']));
    321                 $logger->log('error', 'Failed to retrieve Square catalog: ' . json_encode($response['data']), [
    322                     'error_message' => json_encode($response['data']),
    323                     'process_id' => $uniqueProcessId
    324                 ]);
    325                 $this->inventory_update_in_progress = false;
    326             } else {
    327 
    328                 if (isset($response['data']['objects']) && !empty($response['data']['objects'])) {
    329 
    330                     $body = [
    331                         'idempotency_key' => wp_generate_uuid4(),
    332                         'changes' => []
    333                     ];
    334 
    335                     foreach ($response['data']['objects'] as $square_object) {
    336                         // Ensure 'item_data' exists and has 'variations'
    337                         if (isset($square_object['item_data']['variations']) && is_array($square_object['item_data']['variations'])) {
    338                             foreach ($square_object['item_data']['variations'] as $variation) {
    339                                 // Find the WooCommerce product ID associated with this Square variation
    340                                 $woo_product_key = array_search($square_object['id'], array_column($square_product_ids, 'square_product_id'));
    341 
    342                                 if ($woo_product_key !== false) {
    343                                     $woo_product_id = $square_product_ids[$woo_product_key]['woo_product_id'];
    344                                     $woo_product = wc_get_product($woo_product_id);
    345                                     $stock_quantity = $woo_product->get_stock_quantity();
    346 
    347                                     // Add the change to the body array
    348                                     if ($stock_quantity !== null) {
    349                                         $body['changes'][] = [
    350                                             'physical_count' => [
    351                                                 'quantity' => (string)$stock_quantity,
    352                                                 'occurred_at' => current_time('c'),
    353                                                 'location_id' => $current_settings['location'],
    354                                                 'catalog_object_id' => $variation['id'],
    355                                                 'state' => 'IN_STOCK'
    356                                             ],
    357                                             'type' => 'PHYSICAL_COUNT'
    358                                         ];
    359                                     } else {
    360                                         $logger->log('error', 'Stock quantity is null for product ID: ' . $woo_product_id, [
    361                                             'process_id' => $uniqueProcessId
    362                                         ]);
    363                                     }
    364                                 }
    365                             }
    366                         }
    367                     }
    368 
    369                     // Ensure that there are changes to be synced
    370                     if (!empty($body['changes'])) {
    371                         // Queue the request to update the inventory on Square
    372                         SquareHelper::queue_request('/inventory/changes/batch-create', 'POST', $body, null, function ($response) use ($uniqueProcessId, $logger) {
    373                             if (!$logger) {
    374                                 $logger = new Logger();
    375                             }
    376 
    377                             if ($response['success']) {
    378                                 $logger->log('success', 'Square inventory successfully updated.', [
    379                                     'process_id' => $uniqueProcessId
    380                                 ]);
    381                             } else {
    382                                 $logger->log('error', 'Failed to update Square inventory: ' . json_encode($response), [
    383                                     'error_message' => json_encode($response),
    384                                     'process_id' => $uniqueProcessId
    385                                 ]);
    386                             }
    387 
    388                             // Reset inventory update flag here
    389                             $this->inventory_update_in_progress = false;
    390                         });
    391                     } else {
    392                         $logger->log('info', 'No valid inventory changes found to sync with Square.', [
    393                             'process_id' => $uniqueProcessId
    394                         ]);
    395 
    396                         // Reset inventory update flag here
    397                         $this->inventory_update_in_progress = false;
    398                     }
    399                 } else {
    400                     $logger->log('info', 'No valid objects returned from Square API.', [
    401                         'process_id' => $uniqueProcessId
    402                     ]);
    403 
    404                     // Reset inventory update flag here
    405                     $this->inventory_update_in_progress = false;
    406                 }
    407             }
    408         });
    409     }
    410 
    411 
    412     public function create_order_in_background($order_id)
    413     {
    414         $order = wc_get_order($order_id);
    415         $uniqueProcessId = wp_generate_uuid4();
    416         $logger = new Logger();
    417 
    418         $logger->log('info', 'Initiating order sync to Square for order #' . $order_id, array('process_id' => $uniqueProcessId));
    419 
    420         $ordersController = new OrdersController();
    421         $square = new SquareHelper();
    422 
    423         try {
    424             // Retrieve or create Square customer ID
    425             $square_customer_id = $ordersController->getOrCreateSquareCustomer($order, $square);
    426 
    427 
    428             if (isset($square_customer_id['error'])) {
    429                 $logger->log('error', 'Square Orders error: ' .  $square_customer_id['error'], array('parent_id' => $uniqueProcessId));
    430             }
    431 
    432             // Prepare order data for Square
    433             $order_data = $ordersController->prepareSquareOrderData($order, $square_customer_id);
    434             $response = $ordersController->createOrderInSquare($order_data, $square);
    435 
    436             // Check for errors in the response
    437             if (isset($response['error'])) {
    438                 $logger->log('error', 'Square Orders API error: ' .  $response['error'], array('parent_id' => $uniqueProcessId));
    439             }
    440 
    441             $payReponse = $ordersController->payForOrder($response['data']['order'], $square);
    442 
    443             if (isset($payReponse['error'])) {
    444                 $logger->log('error', 'Square Payment API error: ' . $payReponse['error'], array('parent_id' => $uniqueProcessId));
    445             }
    446 
    447 
    448             if (isset($response['data']['order']['id']) && isset($payReponse['data']['payment']['id'])) {
    449                 $square_data =  ['order' => $response, 'payment' => $payReponse];
    450 
    451                 // Save Square order ID and payment ID to WooCommerce order meta
    452                 $order->update_meta_data('square_data', wp_json_encode($square_data));
    453 
    454                 // Save changes to the order
    455                 $order->save();
    456             }
    457 
    458             // if (!empty($current_settings) || !empty($current_settings['loyalty']) || $current_settings['loyalty']['enabled'] === true) {
    459             //     $loyalty = new LoyaltyProgram();
    460             //     $loyalty->accumulate_loylty_points($response['data']['order']['id'], $current_settings['loyalty']['program_id'], $square_customer_id);
    461             // }
    462 
    463             $logger->log('success', 'Order and Transaction created in Square, receipt: #' . $payReponse['data']['payment']['receipt_number'], array('parent_id' => $uniqueProcessId));
    464         } catch (\Exception $e) {
    465             if ($e->getMessage() == 'Square location not set') {
    466                 $logger->log('error', 'Square location not set: ' . $e->getMessage(), array('parent_id' => $uniqueProcessId));
    467             }
    468             $logger->log('error', 'Failed to create order: ' . $e->getMessage(), array('parent_id' => $uniqueProcessId));
    469         }
    470     }
    471 
    472 
    473     /**
    474      * Handle actions on order status change.
    475      *
    476      * @param int    $order_id The order ID.
    477      * @param string $old_status The old order status.
    478      * @param string $new_status The new order status.
    479      * @param  $order The order object.
    480      */
    481     public function create_square_order_after_woo_order($order_id, $old_status = '', $new_status = '', $order = null)
    482     {
    483         error_log('test');
    484         // Check if the event is already scheduled
    485         $timestamp = wp_next_scheduled('sws_sync_inventory_after_product_sold_event', [$order_id]);
    486 
    487         if (!$timestamp) {
    488             // Schedule the sync_inventory_after_product_sold function to run in the background
    489             wp_schedule_single_event(time() + 10, 'sws_sync_inventory_after_product_sold_event', [$order_id]);
    490         }
    491 
    492 
    493 
    494         // Check if the order already has Square data to prevent duplication
    495         if ($order && $order->get_meta('square_data')) {
    496             return;
    497         }
    498 
    499         $current_settings = get_option('square-woo-sync_settings', []);
    500 
    501         if (empty($current_settings) || empty($current_settings['orders']) || $current_settings['orders']['enabled'] !== true) {
    502             return;
    503         }
    504 
    505         if (empty($current_settings) || empty($current_settings['orders']) || $current_settings['orders']['stage'] !== $new_status) {
    506             return;
    507         }
    508 
    509         // Check if the event is already scheduled
    510         $timestamp = wp_next_scheduled('sws_sync_order_after_product_sold_event', [$order_id]);
    511 
    512         if (!$timestamp) {
    513             // Schedule the sync_inventory_after_product_sold function to run in the background
    514             wp_schedule_single_event(time(), 'sws_sync_order_after_product_sold_event', [$order_id]);
    515         }
    516     }
    517 
    518     /**
    519      * AJAX handler for exporting products to Square.
    520      */
    521     public function handle_ajax_export_to_square()
    522     {
    523         check_ajax_referer('sws_ajax_nonce', 'nonce');
    524 
    525         $product_id = intval($_POST['product_id']);
    526         $product = wc_get_product($product_id);
    527 
    528         $uniqueProcessId = wp_generate_uuid4();
    529 
    530         if ($product_id && $product) {
    531             $exporter = new WooImport();
    532 
    533             $result = $exporter->import_products([$product], 1, $uniqueProcessId);
    534             $exporter->update_square_inventory_counts([$product]);
    535 
    536 
    537             if (is_array($result) && isset($result[0]) && is_array($result[0]) && isset($result[0]['success']) && $result[0]['success'] === true) {
    538                 wp_send_json_success(array('message' => 'Successfully exported and linked product to Square.'));
    539             } else {
    540                 wp_send_json_error(array('message' => json_encode($result)));
    541             }
    542         } else {
    543             wp_send_json_error(array('message' => 'Invalid product ID.'));
    544         }
    545     }
    546 
    547     /**
    548      * Delete Square Product from Woo ID
    549      */
    550     public function delete_square_product($post_id)
    551     {
    552         // Check if the post type is 'product'
    553         if (get_post_type($post_id) === 'product') {
    554 
    555             // Get the current settings
    556             $current_settings = get_option('square-woo-sync_settings', []);
    557 
    558             // Check if auto product deletion is enabled in the settings
    559             if (!empty($current_settings) && !empty($current_settings['wooAuto']['autoDeleteProduct']) && $current_settings['wooAuto']['autoDeleteProduct'] === true) {
    560 
    561                 // Get the square_product_id from post meta
    562                 $square_product_id = get_post_meta($post_id, 'square_product_id', true);
    563 
    564                 if ($square_product_id) {
    565                     $product = wc_get_product($post_id);
    566                     $product_name = $product ? $product->get_name() : 'Unknown Product';
    567 
    568                     $logger = new Logger();
    569                     $square_helper = new SquareHelper();
    570                     $uniqueProcessId = wp_generate_uuid4();
    571 
    572                     try {
    573                         // Log the initiation of the deletion process
    574                         $logger->log('info', 'Initiating deletion of Square product ' . $product_name, array(
    575                             'product_id' => $post_id,
    576                             'product_name' => $product_name,
    577                             'square_product_id' => $square_product_id,
    578                             'process_id' => $uniqueProcessId
    579                         ));
    580 
    581                         // Send a request to the Square API to delete the product
    582                         $response = $square_helper->square_api_request("/catalog/object/" . $square_product_id, 'DELETE');
    583 
    584                         if (!$response['success']) {
    585                             // Log the error if the deletion failed
    586                             $logger->log('error', 'Failed to delete ' . $product_name . ' from Square library', array(
    587                                 'product_id' => $post_id,
    588                                 'product_name' => $product_name,
    589                                 'square_product_id' => $square_product_id,
    590                                 'error_message' => $response['error'],
    591                                 'parent_id' => $uniqueProcessId
    592                             ));
    593                         } else {
    594                             // Log the success if the deletion was successful
    595                             $logger->log('success', 'Successfully deleted ' . $product_name . ' from Square library', array(
    596                                 'product_id' => $post_id,
    597                                 'product_name' => $product_name,
    598                                 'square_product_id' => $square_product_id,
    599                                 'parent_id' => $uniqueProcessId
    600                             ));
    601                         }
    602                     } catch (\Exception $e) {
    603                         // Log the exception if an error occurred during the deletion process
    604                         $logger->log('error', 'Exception occurred while deleting Square product', array(
    605                             'product_id' => $post_id,
    606                             'product_name' => $product_name,
    607                             'square_product_id' => $square_product_id,
    608                             'error_message' => $e->getMessage(),
    609                             'process_id' => $uniqueProcessId
    610                         ));
    611                     }
    612                 }
    613             }
    614         }
     146    if (!$timestamp) {
     147      // Schedule the sync_inventory_after_product_sold function to run in the background
     148      wp_schedule_single_event(time() + 10, 'sws_sync_inventory_after_product_sold_event', [$order_id]);
    615149    }
    616150
    617151
    618152
     153    // Check if the order already has Square data to prevent duplication
     154    if ($order && $order->get_meta('square_data')) {
     155      return;
     156    }
     157
     158    $current_settings = get_option('square-woo-sync_settings', []);
     159    $available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
    619160
    620161
    621 
    622 
    623     /**
    624      * AJAX handler for syncing products to Square.
    625      */
    626     public function handle_ajax_sync_to_square()
    627     {
    628         check_ajax_referer('sws_ajax_nonce', 'nonce');
    629 
    630         $logger = new Logger();
    631 
    632         $product_id = intval($_POST['product_id']);
    633 
    634         if ($product_id) {
    635 
    636             $product = wc_get_product($product_id);
    637             $data_to_import = array(
    638                 'stock' => true,
    639                 'title' => true,
    640                 'description' => true,
    641                 'price' => true,
    642                 'sku' => true,
    643             );
    644 
    645 
    646 
    647             $result = $this->on_product_update($product_id, $data_to_import, true);
    648 
    649             if ($result && $this->is_sync_successful($result)) {
    650                 $logger->log('success', 'Successfully synced: ' .  $product->get_title() . ' to Square', array('product_id' => $product_id));
    651                 wp_send_json_success(array('message' => 'Product synced successfully with Square.'));
    652             } else {
    653                 wp_send_json_error(array('message' => $result['error']));
    654             }
    655         } else {
    656             wp_send_json_error(array('message' => 'Invalid product ID.'));
    657         }
     162    if (empty($current_settings) || empty($current_settings['orders']) || $current_settings['orders']['enabled'] !== true && !isset($available_gateways['squaresync_credit'])) {
     163      return;
    658164    }
    659165
    660     /**
    661      * Check if sync result is successful.
    662      *
    663      * @param array $result The result array.
    664      *
    665      * @return bool
    666      */
    667     private function is_sync_successful($result)
    668     {
    669         return (isset($result['inventoryUpdateStatus']['success']) && $result['inventoryUpdateStatus']['success'] === true) ||
    670             (isset($result['productUpdateStatus']['success']) && $result['productUpdateStatus']['success'] === true);
     166    if (empty($current_settings) || empty($current_settings['orders']) || $current_settings['orders']['stage'] !== $new_status) {
     167      return;
    671168    }
    672169
     170    // Check if the event is already scheduled
     171    $timestamp = wp_next_scheduled('sws_sync_order_after_product_sold_event', [$order_id]);
    673172
    674     /**
    675      * Renders the HTML for the meta box.
    676      *
    677      * @param WP_Post $post The post object.
    678      */
    679     public function sync_meta_box_html($post)
    680     {
    681         $square_product_id = get_post_meta($post->ID, 'square_product_id', true);
    682 
    683         if (!empty($square_product_id)) {
    684             echo '<p>' . esc_html__('Sync this product to Square', 'squarewoosync') . '</p>';
    685             echo '<button id="sync_to_square_button" class="update-button button button-primary button-large" data-product-id="' . esc_attr($post->ID) . '">' . esc_html__('Sync to Square', 'squarewoosync') . '</button>';
    686             echo '<p class="sws-notice">' . esc_html__('Update the product and then run the above sync. For a full tutorial, please read the documentation.', 'squarewoosync') . '</p>';
    687         } else {
    688             echo '<p>' . esc_html__('No Square product ID found. Unable to sync to square. Only products imported from square can be synced.', 'squarewoosync') . '</p>';
    689             echo '<button id="export_to_square_button" class="update-button button button-primary button-large" data-product-id="' . esc_attr($post->ID) . '">' . esc_html__('Export to Square', 'squarewoosync') . '</button>';
    690         }
     173    if (!$timestamp) {
     174      // Schedule the sync_inventory_after_product_sold function to run in the background
     175      wp_schedule_single_event(time(), 'sws_sync_order_after_product_sold_event', [$order_id]);
    691176    }
    692 
    693 
    694     /**
    695      * Handles the product update process.
    696      *
    697      * @param int $product_id The product ID.
    698      * @return mixed
    699      */
    700     public function on_product_update($product_id, $data_to_import, $force = false)
    701     {
    702 
    703 
    704         $settings = get_option('square-woo-sync_settings', []);
    705 
    706         if (empty($settings['wooAuto']) && !$force) {
    707             return null;
    708         }
    709 
    710         $product = wc_get_product($product_id);
    711 
    712 
    713         if (!$product instanceof \WC_Product) {
    714             return null; // Optionally log this error.
    715         }
    716 
    717         $square_product_id = get_post_meta($product_id, 'square_product_id', true);
    718         $woo_data = $this->get_woo_product_data($product, $square_product_id);
    719         if ($square_product_id && !empty($woo_data)) {
    720             return $this->update_square_product($square_product_id, $woo_data, $data_to_import);
    721         }
    722     }
    723 
    724     /**
    725      * Retrieves WooCommerce product data.
    726      *
    727      * @param \WC_Product $product          WooCommerce product object.
    728      * @param string      $square_product_id Square product ID.
    729      * @return array
    730      */
    731     public function get_woo_product_data(\WC_Product $product, $square_product_id)
    732     {
    733         $woo_data = [
    734             'name'        => $product->get_name(),
    735             'description' => $product->get_description(),
    736             'variations'  => []
    737         ];
    738 
    739         if ($product->is_type('variable')) {
    740             foreach ($product->get_children() as $variation_id) {
    741                 $variation = wc_get_product($variation_id);
    742                 if (!$variation) {
    743                     continue;
    744                 }
    745 
    746                 $variation_product_id = get_post_meta($variation->get_id(), 'square_product_id', true);
    747                 $woo_data['variations'][] = $this->format_variation_data($variation, $variation_product_id);
    748             }
    749         } else {
    750             $woo_data['variations'][] = $this->format_variation_data($product, $square_product_id);
    751         }
    752 
    753         return $woo_data;
    754     }
    755 
    756     /**
    757      * Formats variation data for synchronization.
    758      *
    759      * @param \WC_Product $product          WooCommerce product object.
    760      * @param string      $square_product_id Square product ID.
    761      * @return array
    762      */
    763     private function format_variation_data(\WC_Product $product, $square_product_id)
    764     {
    765         return [
    766             'price'     => $product->get_price(),
    767             'sku'       => $product->get_sku(),
    768             'stock'     => $product->get_stock_quantity(),
    769             'square_id' => $square_product_id,
    770         ];
    771     }
    772 
    773     /**
    774      * Updates the Square product with WooCommerce data.
    775      *
    776      * @param string $square_product_id Square product ID.
    777      * @param array  $woo_data          WooCommerce product data.
    778      * @return mixed
    779      */
    780     public function update_square_product($square_product_id, $woo_data, $data_to_import)
    781     {
    782         $square_helper = new SquareHelper();
    783         $square_product_data = $square_helper->get_square_item_details($square_product_id);
    784 
    785         if (isset($square_product_data['object']) && isset($square_product_data['object']['type'])) {
    786             // Check if it's an ITEM or ITEM_VARIATION and proceed accordingly
    787             if ($square_product_data['object']['type'] === 'ITEM') {
    788                 if (count($woo_data['variations']) === 1 && isset($square_product_data['object']['item_data']['variations'][0]['id'])) {
    789                     $woo_data['variations'][0]['square_id'] = $square_product_data['object']['item_data']['variations'][0]['id'];
    790                 }
    791             } elseif ($square_product_data['object']['type'] === 'ITEM_VARIATION' && isset($square_product_data['object']['item_variation_data']['id'])) {
    792                 // Assuming the structure to access the ID for an ITEM_VARIATION is correct
    793                 // Adjust based on actual structure if needed
    794                 if (count($woo_data['variations']) === 1) {
    795                     $woo_data['variations'][0]['square_id'] = $square_product_data['object']['item_variation_data']['id'];
    796                 }
    797             }
    798 
    799             $updated_response = $square_helper->update_square_product($woo_data, $square_product_data['object'], $data_to_import);
    800             return $updated_response;
    801         } else {
    802             return $square_product_data; // Return the original data if 'object' or 'type' key is not set
    803         }
    804     }
    805 
    806     public function export_to_square($new_status, $old_status, $post)
    807     {
    808         // Ensure WooCommerce is loaded
    809         if (!class_exists('WooCommerce')) {
    810             error_log('WooCommerce not loaded.');
    811             return;
    812         }
    813 
    814         // Check if the post type is 'product' and the new status is 'publish'
    815         if ($post->post_type === 'product' && $new_status === 'publish') {
    816             // Get the post's published date
    817             $published_date = get_the_date('Y-m-d', $post);
    818 
    819             // Get the current date
    820             $current_date = current_time('Y-m-d');
    821 
    822             // If the post has not been previously published (published date is current date)
    823             if ($published_date == $current_date) {
    824 
    825                 // Get current settings to check if auto-create is enabled
    826                 $current_settings = get_option('square-woo-sync_settings', []);
    827                 if (empty($current_settings) || empty($current_settings['wooAuto']['autoCreateProduct']) || !$current_settings['wooAuto']['autoCreateProduct']) {
    828                     return;
    829                 }
    830 
    831                 // Fetch the product
    832                 $product_id = $post->ID;
    833                 $product = wc_get_product($product_id);
    834                 $square_product_id = get_post_meta($product_id, 'square_product_id', true);
    835 
    836                 if (!$product || $square_product_id) {
    837                     return;
    838                 }
    839 
    840                 $logger = new Logger(); // Ensure this Logger class is defined
    841                 $uniqueProcessId = wp_generate_uuid4();
    842 
    843                 // Assuming WooImport is a class responsible for handling the export
    844                 $exporter = new WooImport(); // Ensure this class is defined or included
    845 
    846                 // Export the product
    847                 $result = $exporter->import_products([$product], 1, $uniqueProcessId);
    848                 $inventoryResult = $exporter->update_square_inventory_counts([$product]);
    849 
    850                 // Handle the export result
    851                 if (is_wp_error($result)) {
    852                     error_log('Error exporting product to Square');
    853                 } else {
    854                     $logger->log('success', 'Product exported and linked to Square: ' . $product->get_name(), array('process_id' => $uniqueProcessId));
    855 
    856                     set_transient('square_sync_success', 'Product "' . $product->get_name() . '" was successfully created in Square and linked for automatic syncing (if enabled).', 30);
    857                 }
    858             }
    859         }
    860     }
     177  }
    861178}
    862 
    863 // Display the WooCommerce notice
    864 add_action('admin_notices', function () {
    865     if ($message = get_transient('square_sync_success')) {
    866         echo '<div class="notice notice-success is-dismissible">';
    867         echo '<p>' . esc_html($message) . '</p>';
    868         echo '</div>';
    869         delete_transient('square_sync_success');
    870     }
    871 });
  • squarewoosync/trunk/languages/square-woo-sync.pot

    r3220600 r3225869  
    22msgid ""
    33msgstr ""
    4 "Project-Id-Version: Square Sync for Woocommerce 5.1.0\n"
     4"Project-Id-Version: Square Sync for Woocommerce 5.1.1\n"
    55"Report-Msgid-Bugs-To: https://github.com/LiamHillier/square-woo-sync/issues\n"
    66"Last-Translator: liam@pixeldev.com.au\n"
     
    99"Content-Type: text/plain; charset=UTF-8\n"
    1010"Content-Transfer-Encoding: 8bit\n"
    11 "POT-Creation-Date: 2025-01-11T18:09:29+11:00\n"
     11"POT-Creation-Date: 2025-01-21T12:49:08+11:00\n"
    1212"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1313"X-Generator: WP-CLI 2.10.0\n"
     
    6060
    6161#: includes/Admin/Menu.php:50
    62 #: squarewoosync.php:211
     62#: squarewoosync.php:212
    6363msgid "Settings"
    6464msgstr ""
     
    102102
    103103#: includes/REST/OrdersController.php:99
    104 #: includes/REST/OrdersController.php:109
     104#: includes/REST/OrdersController.php:112
    105105msgid "Square API error: "
    106106msgstr ""
    107107
    108 #: includes/REST/OrdersController.php:107
     108#: includes/REST/OrdersController.php:110
    109109msgid "Square Payment API error: "
    110110msgstr ""
    111111
    112 #: includes/REST/OrdersController.php:125
     112#: includes/REST/OrdersController.php:128
    113113msgid "Order and Transaction created in Square, receipt: #"
    114114msgstr ""
    115115
    116 #: includes/REST/OrdersController.php:145
     116#: includes/REST/OrdersController.php:148
    117117msgid "Order created in Square"
    118118msgstr ""
    119119
    120 #: includes/REST/OrdersController.php:152
     120#: includes/REST/OrdersController.php:155
    121121msgid "Failed to create order: "
    122122msgstr ""
    123123
    124 #: includes/REST/OrdersController.php:959
     124#: includes/REST/OrdersController.php:962
    125125msgid "Woocommerce not installed or activated"
    126126msgstr ""
     
    175175msgstr ""
    176176
    177 #: squarewoosync.php:212
     177#: squarewoosync.php:213
    178178msgid "Documentation"
    179179msgstr ""
    180180
    181 #: squarewoosync.php:213
     181#: squarewoosync.php:214
    182182msgid "Go Pro"
    183183msgstr ""
    184184
    185 #: squarewoosync.php:253
     185#: squarewoosync.php:254
    186186msgid "Square Product ID"
    187187msgstr ""
    188188
    189 #: squarewoosync.php:255
     189#: squarewoosync.php:256
    190190msgid "Enter the Square product ID here."
    191191msgstr ""
  • squarewoosync/trunk/readme.txt

    r3220600 r3225869  
    66Tested up to: 6.7
    77Requires PHP: 7.4
    8 Stable tag: 5.1.0
     8Stable tag: 5.1.1
    99License: GPLv2 or later
    1010License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    110110
    111111== Changelog ==
     112= 5.1.1 =
     113* Fix auto order sync for non Square-Sync gateways
     114
    112115= 5.1.0 =
    113116* Add woocommerce subscriptions support
  • squarewoosync/trunk/squarewoosync.php

    r3220600 r3225869  
    1212 * License URI:     http://www.gnu.org/licenses/gpl-2.0.html
    1313 * Domain Path:     /languages
    14  * Version:         5.1.0
     14 * Version:         5.1.1
    1515 * Requires at least: 5.4
    1616 * Requires PHP:      7.4
     
    2929final class SquareWooSync
    3030{
    31     const VERSION = '5.0.7';
     31    const VERSION = '5.1.1';
    3232    const SLUG = 'squarewoosync';
    3333
     
    144144        $this->container['assets'] = new Pixeldev\SquareWooSync\Assets\Manager();
    145145        $this->container['rest_api'] = new Pixeldev\SquareWooSync\REST\Api(); 
     146        $this->container['square'] = new Pixeldev\SquareWooSync\Woo\SyncProduct(); 
    146147    }
    147148
  • squarewoosync/trunk/vendor/composer/installed.php

    r3220600 r3225869  
    44        'pretty_version' => 'dev-main',
    55        'version' => 'dev-main',
    6         'reference' => '8a016ff1c7c1fe4a3354a6a50d0d282ed6566aa2',
     6        'reference' => '862e4e7b346b246b0cab176f1b96c60614a4e3ba',
    77        'type' => 'project',
    88        'install_path' => __DIR__ . '/../../',
     
    1414            'pretty_version' => 'dev-main',
    1515            'version' => 'dev-main',
    16             'reference' => '8a016ff1c7c1fe4a3354a6a50d0d282ed6566aa2',
     16            'reference' => '862e4e7b346b246b0cab176f1b96c60614a4e3ba',
    1717            'type' => 'project',
    1818            'install_path' => __DIR__ . '/../../',
Note: See TracChangeset for help on using the changeset viewer.