ID); if (!$product) return; $aikanenId = $product->get_meta('_aikanen_service_id'); $lastSynced = $product->get_meta('_aikanen_last_synced'); if ($aikanenId) { // If synced printf( '
%s
%s
%s %s
', esc_html__('Last synced', 'aikanen'), esc_html(wp_date(get_option('date_format') . ' ' . get_option('time_format'), (int) $lastSynced)) ); } // Desync option $url = wp_nonce_url( admin_url('admin-post.php?action=aikanen_desync_product&product_id=' . $post->ID), 'aikanen_desync_product_' . $post->ID ); printf( '%s', esc_url($url), esc_js(__('Are you sure you want to desync this product from Aikanen?', 'aikanen')), esc_html__('Desync from Aikanen', 'aikanen') ); } else { // Not synced printf('%s
', esc_html__('Not synced to Aikanen.', 'aikanen')); } } // Remove sync from product public function handleDesync(): void { $productId = absint(wp_unslash($_GET['product_id'] ?? 0)); if ( !$productId || !current_user_can('manage_woocommerce') || !check_admin_referer('aikanen_desync_product_' . $productId) ) { wp_die('Unauthorized.'); } $product = wc_get_product($productId); if (!$product) { wp_die('Product not found.'); } // Remove Aikanen related meta from product $product->delete_meta_data('_aikanen_service_id'); $product->delete_meta_data('_aikanen_last_synced'); $product->save_meta_data(); wp_safe_redirect(admin_url('post.php?post=' . $productId . '&action=edit')); // Return to product page exit; } // Attach product to service group on Aikanen when linked to a matching category on Woo (or detach when unlinked from category) public function onProductTermsSet(int $objectId, array $terms, array $ttIds, string $taxonomy, bool $append, array $oldTtIds): void { if ($taxonomy !== 'product_cat' || SyncState::$suppressOutbound) { return; } $product = wc_get_product($objectId); if (!$product) return; $aikanenId = $product->get_meta('_aikanen_service_id'); if (!$aikanenId) return; $resolve = function (array $ttIds): array { $groupIds = []; foreach ($ttIds as $ttId) { $term = get_term_by('term_taxonomy_id', $ttId, 'product_cat'); if (!$term) continue; $groupId = get_term_meta($term->term_id, '_aikanen_group_id', true); if ($groupId) $groupIds[] = $groupId; } return $groupIds; }; $newGroupIds = $resolve($ttIds); $oldGroupIds = $resolve($oldTtIds); $attached = array_diff($newGroupIds, $oldGroupIds); $detached = array_diff($oldGroupIds, $newGroupIds); if (!empty($attached)) { // If attached to any linked categories try { $this->api->post("/services/{$aikanenId}/service-groups/attach", ['service_group_ids' => array_values($attached)]); } catch (\RuntimeException $e) { // Service no longer exists in Aikanen - silently skip } } if (!empty($detached)) { // If detached from any linked categories try { $this->api->post("/services/{$aikanenId}/service-groups/detach", ['service_group_ids' => array_values($detached)]); } catch (\RuntimeException $e) { // Service no longer exists in Aikanen - silently skip } } } }