WP_REST_Server::CREATABLE, 'permission_callback' => function(){ return current_user_can('manage_woocommerce'); }, 'callback' => array(__CLASS__, 'handle'), )); }); } public static function patch_so_from_order($order_id){ $order = wc_get_order($order_id); if (!$order) return array('ok'=>false,'error'=>'order_not_found'); global $wpdb; $map = $wpdb->prefix . 'wzhf_orders'; $so_id = $wpdb->get_var($wpdb->prepare("SELECT zoho_salesorder_id FROM $map WHERE wc_order_id=%d", $order_id)); if(!$so_id) return array('ok'=>false,'error'=>'no_mapping'); // Build clamped shipping address from order (fallback billing) $ship_addr1 = trim($order->get_shipping_address_1()); $ship_addr2 = trim($order->get_shipping_address_2()); if($ship_addr1==='' && $ship_addr2===''){ $ship_addr1 = trim($order->get_billing_address_1()); $ship_addr2 = trim($order->get_billing_address_2()); } $ship_city = trim($order->get_shipping_city()) ?: trim($order->get_billing_city()); $ship_state = trim($order->get_shipping_state()) ?: trim($order->get_billing_state()); $ship_zip = trim($order->get_shipping_postcode()) ?: trim($order->get_billing_postcode()); $ship_ctry = trim($order->get_shipping_country()) ?: trim($order->get_billing_country()); $addr_line = trim(preg_replace('/\s+/u',' ', trim($ship_addr1.' '.($ship_addr2?:'')))); if(function_exists('mb_substr')){ $address = mb_substr($addr_line,0,90,'UTF-8'); $city = mb_substr($ship_city,0,40,'UTF-8'); $state = mb_substr($ship_state,0,30,'UTF-8'); $zip = mb_substr($ship_zip,0,20,'UTF-8'); $country = mb_substr($ship_ctry,0,30,'UTF-8'); } else { $address = substr($addr_line,0,90); $city = substr($ship_city,0,40); $state = substr($ship_state,0,30); $zip = substr($ship_zip,0,20); $country = substr($ship_ctry,0,30); } $payload = array('shipping_address'=>array( 'address'=>$address,'city'=>$city,'state'=>$state,'zip'=>$zip,'country'=>$country )); WZHF_Logger::log('SO patch:addr-lengths', array( 'order_id'=>$order_id, 'address'=> (function_exists('mb_strlen')?mb_strlen($address,'UTF-8'):strlen($address)), 'city'=> (function_exists('mb_strlen')?mb_strlen($city,'UTF-8'):strlen($city)), 'state'=> (function_exists('mb_strlen')?mb_strlen($state,'UTF-8'):strlen($state)), 'zip'=> (function_exists('mb_strlen')?mb_strlen($zip,'UTF-8'):strlen($zip)), 'country'=> (function_exists('mb_strlen')?mb_strlen($country,'UTF-8'):strlen($country)) )); $z = new WZHF_Zoho(); if(!$z->is_connected()) return array('ok'=>false,'error'=>'zoho_disconnected'); // Reuse private request via reflection $ref = new ReflectionClass('WZHF_Zoho'); $m = $ref->getMethod('request'); $m->setAccessible(true); $res = $m->invoke($z, 'PUT', '/salesorders/'.rawurlencode($so_id), $payload); $body = $res[0]; $err = $res[1]; if($body && !empty($body['salesorder'])){ WZHF_Logger::log('SO patch:ok', array('order_id'=>$order_id,'so'=>$so_id)); return array('ok'=>true,'so'=>$so_id); } else { WZHF_Logger::log('SO patch:error', array('order_id'=>$order_id,'so'=>$so_id,'err'=>$err)); return array('ok'=>false,'error'=>'patch_failed','details'=>$err); } } public static function handle(WP_REST_Request $req){ if (!current_user_can('manage_woocommerce')) { return new WP_REST_Response(array('error' => 'forbidden'), 403); } $op = sanitize_text_field($req->get_param('op') ? $req->get_param('op') : ''); switch ($op) { case 'reset_map': { $order_id = absint($req->get_param('order_id')); if (!$order_id) return new WP_REST_Response(array('error' => 'order_id required'), 400); global $wpdb; $map = $wpdb->prefix . 'wzhf_orders'; $wpdb->delete($map, array('wc_order_id' => $order_id)); delete_post_meta($order_id, '_tracking_number'); delete_post_meta($order_id, '_tracking_carrier'); WZHF_Logger::log('tools:reset_map', array('order_id' => $order_id)); return new WP_REST_Response(array('ok' => true, 'message' => 'Mapping reset'), 200); } case 'resend_so': { $order_id = absint($req->get_param('order_id')); if (!$order_id) return new WP_REST_Response(array('error' => 'order_id required'), 400); $order = wc_get_order($order_id); if (!$order) return new WP_REST_Response(array('error' => 'order_not_found'), 404); global $wpdb; $map = $wpdb->prefix . 'wzhf_orders'; // Drop mapping to allow re-send $wpdb->delete($map, array('wc_order_id' => $order_id)); WZHF_Logger::log('tools:resend_so', array('order_id' => $order_id)); // Try to create WZHF_Orders_Sync::create_sales_order($order_id); // Check result $created = $wpdb->get_var($wpdb->prepare("SELECT zoho_salesorder_id FROM $map WHERE wc_order_id=%d", $order_id)); if ($created) { return new WP_REST_Response(array('ok' => true, 'message' => 'Created SO: ' . $created), 200); } // Not created; hint common reasons $z = new WZHF_Zoho(); if (!$z->is_connected()) { return new WP_REST_Response(array('error' => 'zoho_disconnected', 'message' => 'Zoho not connected (refresh OAuth).'), 500); } return new WP_REST_Response(array('error' => 'resend_failed', 'message' => 'SO not created. See WooZoho → Логи for details.'), 500); } case 'patch_so_address': { $order_id = absint($req->get_param('order_id')); if (!$order_id) return new WP_REST_Response(array('error' => 'order_id required'), 400); $order = wc_get_order($order_id); if (!$order) return new WP_REST_Response(array('error' => 'order_not_found'), 404); global $wpdb; $map = $wpdb->prefix . 'wzhf_orders'; $so_id = $wpdb->get_var($wpdb->prepare("SELECT zoho_salesorder_id FROM $map WHERE wc_order_id=%d", $order_id)); if(!$so_id) return new WP_REST_Response(array('error'=>'no_mapping','message'=>'SO mapping not found. Создайте SO сначала.'), 400); // Build clamped shipping address from order $ship_addr1 = trim($order->get_shipping_address_1()); $ship_addr2 = trim($order->get_shipping_address_2()); if($ship_addr1==='' && $ship_addr2===''){ // fallback to billing $ship_addr1 = trim($order->get_billing_address_1()); $ship_addr2 = trim($order->get_billing_address_2()); } $ship_city = trim($order->get_shipping_city()) ?: trim($order->get_billing_city()); $ship_state = trim($order->get_shipping_state()) ?: trim($order->get_billing_state()); $ship_zip = trim($order->get_shipping_postcode()) ?: trim($order->get_billing_postcode()); $ship_ctry = trim($order->get_shipping_country()) ?: trim($order->get_billing_country()); $addr_line = trim(preg_replace('/\s+/u',' ', trim($ship_addr1.' '.($ship_addr2?:'')))); // Clamp if(function_exists('mb_substr')){ $address = mb_substr($addr_line,0,90,'UTF-8'); $city = mb_substr($ship_city,0,40,'UTF-8'); $state = mb_substr($ship_state,0,30,'UTF-8'); $zip = mb_substr($ship_zip,0,20,'UTF-8'); $country = mb_substr($ship_ctry,0,30,'UTF-8'); } else { $address = substr($addr_line,0,90); $city = substr($ship_city,0,40); $state = substr($ship_state,0,30); $zip = substr($ship_zip,0,20); $country = substr($ship_ctry,0,30); } $payload = array('shipping_address'=>array( 'address'=>$address,'city'=>$city,'state'=>$state,'zip'=>$zip,'country'=>$country )); WZHF_Logger::log('SO patch:addr-lengths', array( 'order_id'=>$order_id, 'address'=> (function_exists('mb_strlen')?mb_strlen($address,'UTF-8'):strlen($address)), 'city'=> (function_exists('mb_strlen')?mb_strlen($city,'UTF-8'):strlen($city)), 'state'=> (function_exists('mb_strlen')?mb_strlen($state,'UTF-8'):strlen($state)), 'zip'=> (function_exists('mb_strlen')?mb_strlen($zip,'UTF-8'):strlen($zip)), 'country'=> (function_exists('mb_strlen')?mb_strlen($country,'UTF-8'):strlen($country)) )); $z = new WZHF_Zoho(); if(!$z->is_connected()) return new WP_REST_Response(array('error'=>'zoho_disconnected'),500); // Use Zoho request via reflection (quick reuse) $ref = new ReflectionClass('WZHF_Zoho'); $m = $ref->getMethod('request'); $m->setAccessible(true); $res = $m->invoke($z, 'PUT', '/salesorders/'.rawurlencode($so_id), $payload); $body = $res[0]; $err = $res[1]; if($body && !empty($body['salesorder'])){ WZHF_Logger::log('SO patch:ok', array('order_id'=>$order_id,'so'=>$so_id)); return new WP_REST_Response(array('ok'=>true,'message'=>'Patched SO address: '+$so_id),200); } else { WZHF_Logger::log('SO patch:error', array('order_id'=>$order_id,'so'=>$so_id,'err'=>$err)); return new WP_REST_Response(array('error'=>'patch_failed','details'=>$err),500); } } default: return new WP_REST_Response(array('error' => 'unknown_op'), 400); } } }