HEX
Server: Apache
System: Linux vps.rockyroadprinting.net 4.18.0 #1 SMP Mon Sep 30 15:36:27 MSK 2024 x86_64
User: rockyroadprintin (1011)
PHP: 8.2.29
Disabled: exec,passthru,shell_exec,system
Upload Files
File: /home/rockyroadprintin/www/wp-content/plugins/woocommerce-square/includes/Sync/Order_Importer.php
<?php
/**
 * Square Order Importer.
 *
 * Handles importing Square orders into WooCommerce.
 * Used by both webhook handlers and scheduled polling.
 *
 * @package WooCommerce\Square\Sync
 * @since 5.0.0
 */

namespace WooCommerce\Square\Sync;

use WooCommerce\Square\Sync\Order_Mapper;
use WooCommerce\Square\Utilities\Money_Utility;
use Automattic\WooCommerce\Utilities\OrderUtil;

defined( 'ABSPATH' ) || exit;

/**
 * Class to handle importing Square orders into WooCommerce.
 *
 * @since 5.0.0
 */
class Order_Importer {

	/**
	 * Update existing WooCommerce order with Square order data.
	 *
	 * @since 5.0.0
	 * @param \WC_Order $wc_order Existing WooCommerce order.
	 * @param \Square\Models\Order $square_order Square order object.
	 * @return array Update result with 'updated' flag and message.
	 */
	public function update_existing_woocommerce_order( $wc_order, $square_order ) {
		$updates_made = array();

		// Get Square order details.
		$square_state      = $square_order->getState();
		$square_updated_at = $square_order->getUpdatedAt();
		$square_total      = $square_order->getTotalMoney();

		// 1. Update order status based on Square state.
		$wc_status     = $wc_order->get_status();
		$new_wc_status = Order_Mapper::map_square_state_to_wc_status( $square_state );
		if ( $wc_status !== $new_wc_status ) {
			// Check if status change is allowed.
			if ( Order_Mapper::is_status_change_allowed( $wc_status, $new_wc_status ) ) {
				$wc_order->set_status( $new_wc_status );
				$updates_made[] = sprintf( 'Status: %s → %s', $wc_status, $new_wc_status );

				// Add order note for status change.
				$wc_order->add_order_note(
					sprintf(
						/* translators: %s is the Square state. */
						__( 'Order status updated from Square. Square state: %s', 'woocommerce-square' ),
						$square_state
					)
				);
			} else {
				// @todo: move this to a separate order sync status page if exists.
				wc_square()->log(
					sprintf(
						'Status change not allowed: %s → %s for order %d',
						$wc_status,
						$new_wc_status,
						$wc_order->get_id()
					),
					'sync'
				);
			}
		}

		// 2. Add order note if fulfillment completed.
		if ( 'COMPLETED' === $square_state ) {
			$wc_order->add_order_note(
				__( 'Fulfillment completed in Square Dashboard/POS.', 'woocommerce-square' )
			);
		}

		// 3. Update order total if changed (with safety checks).
		if ( $square_total ) {
			$currency            = $square_total->getCurrency();
			$square_total_amount = Money_Utility::cents_to_float( $square_total->getAmount(), $currency );
			$current_total       = (float) $wc_order->get_total();
			$decimals            = Money_Utility::get_currency_decimals( $currency );

			$square_total_rounded  = round( $square_total_amount, $decimals );
			$current_total_rounded = round( $current_total, $decimals );

			if ( $square_total_rounded !== $current_total_rounded ) {
				// Only update total if order is not paid yet or if it's a refund scenario.
				if ( ! $wc_order->is_paid() || $square_total_rounded < $current_total_rounded ) {
					$wc_order->set_total( $square_total_rounded );
					$updates_made[] = sprintf( 'Total: %s → %s', $current_total, $square_total_rounded );
				}
			}
		}

		// 4. Update modification timestamps
		$wc_order->update_meta_data( '_square_last_updated', $square_updated_at );
		$wc_order->update_meta_data( '_square_sync_date', current_time( 'mysql' ) );
		$wc_order->update_meta_data( '_square_sync_status', 'updated' );

		// 5. Update fulfillment data from Square
		$this->maybe_update_fulfillment_data( $wc_order, $square_order );

		// Save changes if any updates were made
		if ( ! empty( $updates_made ) ) {
			$wc_order->save();

			return array(
				'updated' => true,
				'message' => 'Updated: ' . implode( ', ', $updates_made ),
				'changes' => $updates_made,
			);
		}

		// Update sync timestamp even if no changes
		$wc_order->update_meta_data( '_square_last_checked', current_time( 'mysql' ) );
		$wc_order->save_meta_data();

		return array(
			'updated' => false,
			'message' => 'No changes detected',
			'changes' => array(),
		);
	}

	/**
	 * Update fulfillment data from Square order.
	 *
	 * @since 5.0.0
	 * @param \WC_Order $wc_order WooCommerce order.
	 * @param \Square\Models\Order $square_order Square order.
	 */
	private function maybe_update_fulfillment_data( $wc_order, $square_order ) {
		$fulfillments = $square_order->getFulfillments();

		if ( empty( $fulfillments ) ) {
			return;
		}

		foreach ( $fulfillments as $fulfillment ) {
			$fulfillment_state = $fulfillment->getState();
			$fulfillment_type  = $fulfillment->getType();

			// Update fulfillment meta.
			$wc_order->update_meta_data( '_square_fulfillment_state', $fulfillment_state );
			$wc_order->update_meta_data( '_square_fulfillment_type', $fulfillment_type );

			// Handle shipment details.
			$shipment_details = $fulfillment->getShipmentDetails();
			if ( $shipment_details ) {
				$tracking_number = $shipment_details->getTrackingNumber();
				$tracking_url    = $shipment_details->getTrackingUrl();
				$carrier         = $shipment_details->getCarrier();

				if ( $tracking_number ) {
					$wc_order->update_meta_data( '_square_tracking_number', $tracking_number );
				}
				if ( $tracking_url ) {
					$wc_order->update_meta_data( '_square_tracking_url', $tracking_url );
				}
				if ( $carrier ) {
					$wc_order->update_meta_data( '_square_carrier', $carrier );
				}
			}

			// Add order note if fulfillment completed.
			if ( 'COMPLETED' === $fulfillment_state ) {
				$note = __( 'Order fulfillment completed in Square.', 'woocommerce-square' );
				if ( $tracking_number ) {
					// translators: %s is the tracking number.
					$note .= sprintf( __( ' Tracking: %s', 'woocommerce-square' ), $tracking_number );
				}
				$wc_order->add_order_note( $note );
			}

			// Handle pickup details.
			$pickup_details = $fulfillment->getPickupDetails();
			if ( $pickup_details ) {
				$pickup_at     = $pickup_details->getPickupAt();
				$schedule_type = $pickup_details->getScheduleType();

				if ( $pickup_at ) {
					$wc_order->update_meta_data( '_square_pickup_time', $pickup_at );
				}
				if ( $schedule_type ) {
					$wc_order->update_meta_data( '_square_pickup_schedule', $schedule_type );
				}
			}

			// Add order note if pickup completed.
			if ( 'COMPLETED' === $fulfillment_state ) {
				$wc_order->add_order_note(
					__( 'Order pickup completed in Square.', 'woocommerce-square' )
				);
			}
		}
	}

	/**
	 * Find existing WooCommerce order by Square order ID.
	 *
	 * @since 5.0.0
	 * @param string $square_order_id Square order ID.
	 * @return \WC_Order|false
	 */
	public function find_existing_wc_order_by_square_order_id( $square_order_id ) {
		global $wpdb;

		// Check if HPOS is enabled.
		if ( class_exists( OrderUtil::class ) && OrderUtil::custom_orders_table_usage_is_enabled() ) {
			// HPOS: Query wc_orders_meta table.
			$results = $wpdb->get_results(
				$wpdb->prepare(
					"
					SELECT order_id, meta_key
					FROM {$wpdb->prefix}wc_orders_meta
					WHERE meta_value = %s
					AND meta_key LIKE %s
					LIMIT 1
					",
					$square_order_id,
					'%_square_order_id'
				),
				ARRAY_A
			);
		} else {
			// Traditional: Query wp_postmeta table.
			$results = $wpdb->get_results(
				$wpdb->prepare(
					"
					SELECT p.ID as order_id, pm.meta_key
					FROM {$wpdb->posts} p
					INNER JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
					WHERE pm.meta_value = %s
					AND pm.meta_key LIKE %s
					AND p.post_type = 'shop_order'
					LIMIT 1
					",
					$square_order_id,
					'%_square_order_id'
				),
				ARRAY_A
			);
		}

		if ( ! empty( $results ) ) {
			$order = wc_get_order( $results[0]['order_id'] );
			if ( $order instanceof \WC_Order ) {
				return $order;
			}
		}

		return false;
	}
}