378 lines
11 KiB
PHP
378 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* WooCommerce Square
|
|
*
|
|
* This source file is subject to the GNU General Public License v3.0
|
|
* that is bundled with this package in the file license.txt.
|
|
* It is also available through the world-wide-web at this URL:
|
|
* http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
|
|
* If you did not receive a copy of the license and are unable to
|
|
* obtain it through the world-wide-web, please send an email
|
|
* to license@woocommerce.com so we can send you a copy immediately.
|
|
*
|
|
* DISCLAIMER
|
|
*
|
|
* Do not edit or add to this file if you wish to upgrade WooCommerce Square to newer
|
|
* versions in the future. If you wish to customize WooCommerce Square for your
|
|
* needs please refer to https://docs.woocommerce.com/document/woocommerce-square/
|
|
*
|
|
* @author WooCommerce
|
|
* @copyright Copyright: (c) 2019, Automattic, Inc.
|
|
* @license http://www.gnu.org/licenses/gpl-3.0.html GNU General Public License v3.0 or later
|
|
*/
|
|
|
|
namespace WooCommerce\Square;
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
use WooCommerce\Square\Handlers\Product;
|
|
use WooCommerce\Square\Sync\Records;
|
|
|
|
/**
|
|
* AJAX handler.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
class AJAX {
|
|
|
|
|
|
/**
|
|
* Adds AJAX action callbacks.
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function __construct() {
|
|
|
|
// check an individual product sync status
|
|
add_action( 'wp_ajax_wc_square_get_quick_edit_product_details', array( $this, 'get_quick_edit_product_details' ) );
|
|
|
|
// fetch product stock from Square
|
|
add_action( 'wp_ajax_wc_square_fetch_product_stock_with_square', array( $this, 'fetch_product_stock_with_square' ) );
|
|
|
|
add_action( 'wp_ajax_wc_square_import_products_from_square', array( $this, 'import_products_from_square' ) );
|
|
|
|
// sync all products with Square
|
|
add_action( 'wp_ajax_wc_square_sync_products_with_square', array( $this, 'sync_products_with_square' ) );
|
|
|
|
// handle sync records
|
|
add_action( 'wp_ajax_wc_square_handle_sync_records', array( $this, 'handle_sync_records' ) );
|
|
|
|
// get the status of a sync job
|
|
add_action( 'wp_ajax_wc_square_get_sync_with_square_status', array( $this, 'get_sync_with_square_job_status' ) );
|
|
|
|
// rest end point to import products
|
|
add_action( 'rest_api_init', array( $this, 'register_rest_endpoints' ) );
|
|
}
|
|
|
|
/**
|
|
* Register REST API endpoints.
|
|
*
|
|
* @since 2.1.6
|
|
*/
|
|
public function register_rest_endpoints() {
|
|
register_rest_route(
|
|
'wc/v3',
|
|
'wc_square/import-products',
|
|
array(
|
|
'methods' => 'POST',
|
|
'callback' => array( $this, 'import_products_from_square' ),
|
|
'permission_callback' => array( $this, 'check_permission' ),
|
|
)
|
|
);
|
|
|
|
register_rest_route(
|
|
'wc/v3',
|
|
'wc_square/connected_page_visited',
|
|
array(
|
|
'methods' => 'POST',
|
|
'callback' => array( $this, 'update_connected_page_visited' ),
|
|
'permission_callback' => array( $this, 'check_permission' ),
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Verify access.
|
|
*
|
|
* Override this method if custom permissions required.
|
|
*/
|
|
public function check_permission() {
|
|
return current_user_can( 'manage_woocommerce' ); // phpcs:ignore WordPress.WP.Capabilities.Unknown
|
|
}
|
|
|
|
/**
|
|
* Fetches product stock data from Square.
|
|
*
|
|
* @internal
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function fetch_product_stock_with_square() {
|
|
check_ajax_referer( 'fetch-product-stock-with-square', 'security' );
|
|
|
|
if ( ! current_user_can( 'edit_products' ) ) {
|
|
wp_send_json_error( __( 'Invalid permissions.', 'woocommerce-square' ) );
|
|
}
|
|
|
|
$fix_error = __( 'Please mark product as un-synced and save, then synced again.', 'woocommerce-square' );
|
|
$product = isset( $_REQUEST['product_id'] ) ? wc_get_product( (int) $_REQUEST['product_id'] ) : false;
|
|
|
|
if ( $product ) {
|
|
|
|
try {
|
|
$product = Product::update_stock_from_square( $product );
|
|
$response = array(
|
|
'quantity' => $product->get_stock_quantity(),
|
|
'manage_stock' => $product->get_manage_stock(),
|
|
);
|
|
|
|
wp_send_json_success( $response );
|
|
|
|
} catch ( \Exception $exception ) {
|
|
|
|
/* translators: Placeholders: %1$s = error message, %2$s = help text */
|
|
wp_send_json_error( sprintf( __( 'Unable to fetch inventory: %1$s. %2$s', 'woocommerce-square' ), $exception->getMessage(), $fix_error ) );
|
|
}
|
|
}
|
|
|
|
/* translators: Placeholders: %s = help text */
|
|
wp_send_json_error( sprintf( __( 'Error finding item in Square. %s', 'woocommerce-square' ), $fix_error ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Starts importing products from Square.
|
|
*
|
|
* @param \WP_REST_Request $request The REST request object.
|
|
*
|
|
* @internal
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function import_products_from_square( $request = null ) {
|
|
|
|
$api_callback = $request ? $request->get_param( 'api_callback' ) : false;
|
|
|
|
$update_during_import = $request
|
|
? $request->get_param( 'update_during_import' )
|
|
: ( ! empty( $_POST['update_during_import'] ) && 'true' === $_POST['update_during_import'] );
|
|
|
|
if ( ! $api_callback ) {
|
|
check_ajax_referer( 'import-products-from-square', 'security' );
|
|
}
|
|
|
|
// The edit_others_shop_orders capability is used to determine if the user can access these settings.
|
|
if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
|
|
wp_send_json_error( __( 'Could not start import. Invalid permissions.', 'woocommerce-square' ) );
|
|
}
|
|
|
|
$started = wc_square()->get_sync_handler()->start_product_import( $update_during_import );
|
|
|
|
if ( ! $started ) {
|
|
wp_send_json_error( __( 'Could not start import. Please try again.', 'woocommerce-square' ) );
|
|
}
|
|
|
|
wp_send_json_success( __( 'Your products are being imported in the background! This may take some time to complete.', 'woocommerce-square' ) );
|
|
}
|
|
|
|
/**
|
|
* Update the connected_page_visited option.
|
|
* This will be used to show a message to visit the onboarding
|
|
* wizard and the onboarding wizard submenu until found `true`.
|
|
*
|
|
* @since 4.7.0
|
|
*/
|
|
public function update_connected_page_visited() {
|
|
update_option( 'wc_square_connected_page_visited', true, false );
|
|
wp_send_json_success();
|
|
}
|
|
|
|
/**
|
|
* Starts syncing products with Square.
|
|
*
|
|
* @internal
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function sync_products_with_square() {
|
|
|
|
check_ajax_referer( 'sync-products-with-square', 'security' );
|
|
|
|
// The edit_others_shop_orders capability is used to determine if the user can access these settings.
|
|
if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
|
|
wp_send_json_error();
|
|
}
|
|
|
|
$started = wc_square()->get_sync_handler()->start_manual_sync();
|
|
|
|
if ( ! $started ) {
|
|
wp_send_json_error();
|
|
}
|
|
|
|
wp_send_json_success();
|
|
}
|
|
|
|
|
|
/**
|
|
* Handles sync records actions.
|
|
*
|
|
* @internal
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function handle_sync_records() {
|
|
|
|
check_ajax_referer( 'handle-sync-with-square-records', 'security' );
|
|
|
|
if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
|
|
wp_send_json_error( __( 'An error occurred. Invalid permissions.', 'woocommerce-square' ) );
|
|
}
|
|
|
|
$error = '';
|
|
|
|
if ( isset( $_POST['id'], $_POST['handle'] ) ) {
|
|
|
|
$id = sanitize_key( $_POST['id'] );
|
|
$action = sanitize_key( $_POST['handle'] );
|
|
|
|
if ( 'all' === $id && 'delete' === $action ) {
|
|
|
|
$outcome = Records::clean_records();
|
|
$error = esc_html__( 'Could not delete records.', 'woocommerce-square' );
|
|
|
|
} elseif ( is_string( $id ) && '' !== $id ) {
|
|
|
|
switch ( $action ) {
|
|
|
|
case 'delete':
|
|
$outcome = Records::delete_record( $id );
|
|
$error = esc_html__( 'Could not delete record.', 'woocommerce-square' );
|
|
|
|
break;
|
|
|
|
case 'resolve':
|
|
$record = Records::get_record( $id );
|
|
if ( $record ) {
|
|
$record->resolve();
|
|
$outcome = $record->save();
|
|
}
|
|
|
|
$error = esc_html__( 'Could not resolve record.', 'woocommerce-square' );
|
|
|
|
break;
|
|
|
|
case 'unsync':
|
|
$record = Records::get_record( $id );
|
|
$product = $record ? $record->get_product() : null;
|
|
if ( $product ) {
|
|
$record->resolve();
|
|
$outcome = Product::unset_synced_with_square( $product ) && $record->save();
|
|
}
|
|
|
|
$error = esc_html__( 'Could not unsync product.', 'woocommerce-square' );
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( ! empty( $outcome ) ) {
|
|
wp_send_json_success( $outcome );
|
|
}
|
|
}
|
|
|
|
/* translators: Placeholder: %s - error message */
|
|
wp_send_json_error( sprintf( __( 'An error occurred. %s', 'woocommerce-square' ), $error ) );
|
|
}
|
|
|
|
|
|
/**
|
|
* Gets a sync job status.
|
|
*
|
|
* Also bumps the job progression (useful for when background processing isn't available).
|
|
*
|
|
* @internal
|
|
*
|
|
* @since 2.0.0
|
|
*/
|
|
public function get_sync_with_square_job_status() {
|
|
check_ajax_referer( 'get-sync-with-square-status', 'security' );
|
|
|
|
if ( ! current_user_can( 'edit_others_shop_orders' ) ) {
|
|
wp_send_json_error( __( 'An error occurred. Invalid permissions.', 'woocommerce-square' ) );
|
|
}
|
|
|
|
$job_id = isset( $_POST['job_id'] ) ? sanitize_key( $_POST['job_id'] ) : null;
|
|
|
|
if ( $job_id ) {
|
|
try {
|
|
$handler = wc_square()->get_background_job_handler();
|
|
$job_in_progress = $handler ? $handler->get_job( $job_id ) : false;
|
|
if ( $job_in_progress ) {
|
|
|
|
$result = array(
|
|
'action' => $job_in_progress->action,
|
|
'id' => $job_in_progress->id,
|
|
'job_products_count' => count( $job_in_progress->product_ids ),
|
|
'percentage' => ( (float) count( $job_in_progress->processed_product_ids ) / max( 1, count( $job_in_progress->product_ids ) ) ) * 100,
|
|
'imported_products_count' => count( $job_in_progress->processed_product_ids ),
|
|
'updated_products_count' => count( $job_in_progress->updated_product_ids ),
|
|
'skipped_products_count' => count( $job_in_progress->skipped_products ),
|
|
'status' => $job_in_progress->status,
|
|
);
|
|
|
|
wp_send_json_success( $result );
|
|
}
|
|
} catch ( \Exception $e ) {
|
|
|
|
wp_send_json_error( $e->getMessage() );
|
|
}
|
|
}
|
|
|
|
/* translators: Placeholder: %s - sync job ID */
|
|
wp_send_json_error( sprintf( esc_html__( 'No sync job in progress found %s', 'woocommerce-square' ), is_string( $job_id ) ? $job_id : null ) );
|
|
}
|
|
|
|
/**
|
|
* Get sync status, variable status, and edit url for product
|
|
*
|
|
* Used to manipulate quick edit menu for product
|
|
*
|
|
* @since 2.1.6
|
|
*/
|
|
public function get_quick_edit_product_details() {
|
|
|
|
check_ajax_referer( 'get-quick-edit-product-details', 'security' );
|
|
|
|
if ( ! current_user_can( 'edit_products' ) ) {
|
|
wp_send_json_error( 'invalid_permission' );
|
|
}
|
|
|
|
$product = isset( $_POST['product_id'] ) ? wc_get_product( (int) $_POST['product_id'] ) : false;
|
|
|
|
if ( $product ) {
|
|
$is_variable = $product->is_type( 'variable' );
|
|
|
|
if ( ! Product::has_sku( $product ) ) {
|
|
if ( $is_variable ) {
|
|
wp_send_json_error( 'missing_variation_sku' );
|
|
} else {
|
|
wp_send_json_error( 'missing_sku' );
|
|
}
|
|
}
|
|
|
|
$is_synced_with_square = Product::is_synced_with_square( $product ) ? 'yes' : 'no';
|
|
$is_woocommerce_sor = wc_square()->get_settings_handler()->is_system_of_record_woocommerce();
|
|
|
|
wp_send_json_success(
|
|
array(
|
|
'edit_url' => $is_woocommerce_sor ? get_edit_post_link( (int) $_POST['product_id'] ) : null,
|
|
'i18n' => $is_woocommerce_sor ? __( 'Stock must be fetched from Square before editing stock quantity', 'woocommerce-square' ) : null,
|
|
'is_synced_with_square' => $is_synced_with_square,
|
|
'is_variable' => $is_variable,
|
|
)
|
|
);
|
|
}
|
|
wp_send_json_error( 'invalid_product' );
|
|
}
|
|
|
|
}
|