Changeset 3762744
- Timestamp:
- 10/24/2026 04:45:14 AM (5 hours ago)
- Location:
- koalab-sync/trunk
- Files:
-
- 1 added
- 4 edited
-
includes/movements.php (modified) (10 diffs)
-
includes/signals.php (added)
-
koalab-sync.php (modified) (9 diffs)
-
readme.txt (modified) (6 diffs)
-
uninstall.php (modified) (3 diffs)
Legend:
- Unmodified
- Added
- Removed
-
koalab-sync/trunk/includes/movements.php
r3629219 r3762744 22 22 const KOALAB_SYNC_COLLAPSE_THRESHOLD = 1000; // Above this in one request → single "bulk" summary. 23 23 const KOALAB_SYNC_DELIVER_CHUNK = 200; // Events per delivery batch. 24 const KOALAB_SYNC_FIELDS = array( 'regular_price', 'sale_price', 'stock_quantity', 'stock_status' ); 24 /** 25 * «This product does not keep an inventory count», said with a word. 26 * 27 * When `manage_stock` is switched off WooCommerce empties `stock_quantity` (`validate_props()`), so the 28 * change used to travel as «12 → (nothing)», which Koalab cannot tell apart from a value nobody knows and 29 * dropped as noise. Since 1.3.0 the plugin says it explicitly, in both directions, with the same token 30 * Koalab uses everywhere else (`UNTRACKED_STOCK` in the API). Koalab 14 Oct 2026 also reads the old form. 31 */ 32 const KOALAB_SYNC_UNTRACKED = 'untracked'; 33 /** 34 * The product fields whose changes travel to Koalab. 35 * 36 * Scalar only, and on purpose. A movement is a ledger line — «this went from X to Y» — and it is 37 * read in a table next to hundreds of others. A full description or a gallery has no readable 38 * before/after in that shape, so those changes belong to the catalogue reconciliation, not here. 39 * 40 * `sku` is first for a reason: it is the key Koalab matches products by. Changing it in the store 41 * silently detaches the product from its catalogue entry, and until now nothing said so. 42 */ 43 const KOALAB_SYNC_FIELDS = array( 44 'regular_price', 45 'sale_price', 46 'stock_quantity', 47 'stock_status', 48 'sku', 49 'name', 50 'status', 51 'manage_stock', 52 'weight', 53 ); 25 54 26 55 /* ------------------------------------------------------------------------- … … 167 196 * ---------------------------------------------------------------------- */ 168 197 198 /** 199 * Flattens a field value into something a ledger line can hold. 200 * 201 * `manage_stock` arrives as a real boolean and `(string) false` is an empty string — which reads as 202 * «the value was removed» instead of «it was off». Anything that is not a scalar is refused 203 * outright rather than serialised into noise. 204 */ 205 function koalab_sync_scalar( $value ) { 206 if ( is_bool( $value ) ) { 207 return $value ? 'yes' : 'no'; 208 } 209 if ( is_null( $value ) || is_scalar( $value ) ) { 210 return $value; 211 } 212 return null; 213 } 214 169 215 function koalab_sync_push_event( $product, $field, $old, $new ) { 170 216 if ( ! koalab_sync_is_connected() || koalab_sync_is_suppressed() ) { … … 172 218 } 173 219 if ( (string) $old === (string) $new ) { 220 return; 221 } 222 // A non-scalar that `koalab_sync_scalar` refused. Sending it would put `Array` in the ledger. 223 if ( ! is_null( $old ) && ! is_scalar( $old ) ) { 224 return; 225 } 226 if ( ! is_null( $new ) && ! is_scalar( $new ) ) { 174 227 return; 175 228 } … … 211 264 $in_order = ! empty( $GLOBALS['koalab_sync_ctx'] ); 212 265 266 if ( array_key_exists( 'stock_quantity', $changes ) ) { 267 // This save already reports its stock: the `..._set_stock` hooks below must not report it again. 268 $GLOBALS['koalab_sync_stock_seen'][ $product->get_id() ] = true; 269 } 270 213 271 foreach ( KOALAB_SYNC_FIELDS as $field ) { 214 272 if ( ! array_key_exists( $field, $changes ) ) { … … 219 277 } 220 278 $old = isset( $data[ $field ] ) ? $data[ $field ] : null; 221 koalab_sync_push_event( $product, $field, $old, $changes[ $field ] ); 222 } 279 $new = $changes[ $field ]; 280 if ( 'stock_quantity' === $field ) { 281 list( $old, $new ) = koalab_sync_stock_pair( $product, $data, $old, $new ); 282 } 283 koalab_sync_push_event( $product, $field, koalab_sync_scalar( $old ), koalab_sync_scalar( $new ) ); 284 } 285 } 286 287 /** 288 * The stock's before/after, with «no inventory control» named instead of left blank. 289 * 290 * - Switched off: WooCommerce has just emptied the quantity → «N → untracked». 291 * - Switched on, on a product that already existed: the quantity appears from nothing → «untracked → N». 292 * A brand-new product also starts from nothing, and that is not a switch, so it keeps its blank. 293 */ 294 function koalab_sync_stock_pair( $product, $data, $old, $new ) { 295 $was_managed = ! empty( $data['manage_stock'] ) && 'no' !== $data['manage_stock']; 296 $is_managed = (bool) $product->get_manage_stock( 'edit' ); 297 if ( ! $is_managed && ( null === $new || '' === $new ) ) { 298 $new = KOALAB_SYNC_UNTRACKED; 299 } 300 if ( $is_managed && ! $was_managed && ( null === $old || '' === $old ) && $product->get_id() ) { 301 $old = KOALAB_SYNC_UNTRACKED; 302 } 303 return array( $old, $new ); 223 304 } 224 305 … … 255 336 } 256 337 257 /** Refund restock → gives old and new directly. */ 258 function koalab_sync_capture_restock_refunded( $product_id, $old_stock, $new_stock, $item_qty = 0, $order = null ) { 338 /** 339 * Refund restock → gives old and new directly. 340 * 341 * WooCommerce calls it with `( $product_id, $old_stock, $new_stock, $order, $product )`. Up to 1.2.0 the 342 * fourth argument was read as a quantity and the fifth as the order, so the order was never found and the 343 * refund reached Koalab without its order number. Whichever of the two is the order is used. 344 */ 345 function koalab_sync_capture_restock_refunded( $product_id, $old_stock, $new_stock, $arg4 = null, $arg5 = null ) { 259 346 $product = wc_get_product( $product_id ); 260 347 if ( ! $product ) { 261 348 return; 262 349 } 263 koalab_sync_ctx_open( 'refund', is_a( $order, 'WC_Order' ) ? $order->get_id() : 0 ); 350 $order = is_a( $arg4, 'WC_Order' ) ? $arg4 : ( is_a( $arg5, 'WC_Order' ) ? $arg5 : null ); 351 // WooCommerce restocks a refund item by item and calls this after each one. Closing the context 352 // here left the NEXT item's stock write without it, so the `..._set_stock` hooks would report it a 353 // second time as a hand edit. The context the refund opened is restored instead. 354 $prev_ctx = isset( $GLOBALS['koalab_sync_ctx'] ) ? $GLOBALS['koalab_sync_ctx'] : null; 355 $prev_order = isset( $GLOBALS['koalab_sync_order_id'] ) ? $GLOBALS['koalab_sync_order_id'] : 0; 356 koalab_sync_ctx_open( 'refund', $order ? $order->get_id() : 0 ); 264 357 koalab_sync_push_event( $product, 'stock_quantity', $old_stock, $new_stock ); 265 koalab_sync_ctx_close(); 358 if ( $prev_ctx ) { 359 koalab_sync_ctx_open( $prev_ctx, $prev_order ); 360 } else { 361 koalab_sync_ctx_close(); 362 } 363 } 364 365 /** 366 * Stock written straight to the database by `wc_update_product_stock()`. 367 * 368 * That function updates the quantity with SQL and then loads it into the product as already READ, not as 369 * a pending change, so the save that follows has no stock change in it and the CRUD hook above saw 370 * nothing. It is the path of WooCommerce's own bulk edit («Products → Bulk actions → Edit → Stock qty»), 371 * of the order-item quantity edits in the admin, and of most ERP and stock-sync plugins. Up to 1.2.0 372 * those changes never reached Koalab. Read in WooCommerce 11.2.1 (`wc-stock-functions.php`, 373 * `class-wc-admin-post-types.php`). 374 * 375 * The same pair of actions also fires inside an ordinary save that does carry a stock change, and inside 376 * every order operation: the first is already reported by the CRUD hook (`koalab_sync_stock_seen`), the 377 * second by the order hooks (an open context). Both are skipped, so nothing is counted twice. 378 */ 379 function koalab_sync_capture_before_set_stock( $product ) { 380 if ( ! is_a( $product, 'WC_Product' ) || ! empty( $GLOBALS['koalab_sync_ctx'] ) ) { 381 return; 382 } 383 if ( ! empty( $GLOBALS['koalab_sync_stock_seen'][ $product->get_id() ] ) ) { 384 return; 385 } 386 $GLOBALS['koalab_sync_stock_before'][ $product->get_id() ] = $product->get_stock_quantity( 'edit' ); 387 } 388 389 function koalab_sync_capture_set_stock( $product ) { 390 if ( ! is_a( $product, 'WC_Product' ) ) { 391 return; 392 } 393 $id = $product->get_id(); 394 unset( $GLOBALS['koalab_sync_stock_seen'][ $id ] ); 395 if ( ! isset( $GLOBALS['koalab_sync_stock_before'] ) || ! array_key_exists( $id, $GLOBALS['koalab_sync_stock_before'] ) ) { 396 return; 397 } 398 $old = $GLOBALS['koalab_sync_stock_before'][ $id ]; 399 unset( $GLOBALS['koalab_sync_stock_before'][ $id ] ); 400 if ( ! empty( $GLOBALS['koalab_sync_ctx'] ) ) { 401 return; 402 } 403 koalab_sync_push_event( $product, 'stock_quantity', $old, $product->get_stock_quantity( 'edit' ) ); 266 404 } 267 405 … … 315 453 const KOALAB_SYNC_MAX_ATTEMPTS = 5; 316 454 455 /** 456 * Schedules a retry of a batch. 457 * 458 * The batch itself is parked in an option and only its key travels as the action's argument. Action 459 * Scheduler refuses arguments longer than 8,000 characters once JSON-encoded —it logs the exception and 460 * schedules nothing— and one event is ~350 characters, so up to 1.2.0 the retry of any batch of more than 461 * about twenty events was silently never scheduled: exactly the batches that matter (a sale with many 462 * lines, a bulk edit) were lost when Koalab answered with a 5xx. 463 */ 317 464 function koalab_sync_enqueue_delivery( $events, $attempt = 1, $delay = 0 ) { 318 // Entrega inmediata (delay 0): acción ASÍNCRONA de Action Scheduler — corre vía loopback en el 319 // shutdown, en segundos, SIN depender de WP-Cron (clave en tiendas de bajo tráfico). Los 320 // reintentos usan una acción programada con backoff. 465 $key = 'koalab_sync_retry_' . md5( wp_json_encode( $events ) . '|' . $attempt . '|' . microtime( true ) ); 466 update_option( $key, $events, false ); 467 $args = array( $key, $attempt ); 468 321 469 if ( $delay <= 0 && function_exists( 'as_enqueue_async_action' ) ) { 322 as_enqueue_async_action( 'koalab_sync_deliver ', array( $events, $attempt ), 'koalab-sync' );470 as_enqueue_async_action( 'koalab_sync_deliver_parked', $args, 'koalab-sync' ); 323 471 return; 324 472 } 325 473 $when = time() + (int) $delay; 326 474 if ( function_exists( 'as_schedule_single_action' ) ) { 327 as_schedule_single_action( $when, 'koalab_sync_deliver ', array( $events, $attempt ), 'koalab-sync' );475 as_schedule_single_action( $when, 'koalab_sync_deliver_parked', $args, 'koalab-sync' ); 328 476 } else { 329 wp_schedule_single_event( $when, 'koalab_sync_deliver', array( $events, $attempt ) ); 330 } 331 } 477 wp_schedule_single_event( $when, 'koalab_sync_deliver_parked', $args ); 478 } 479 } 480 481 /** Runs a parked retry: takes the batch out of its option (once) and delivers it. */ 482 function koalab_sync_deliver_parked( $key, $attempt = 1 ) { 483 if ( ! is_string( $key ) || 0 !== strpos( $key, 'koalab_sync_retry_' ) ) { 484 return; 485 } 486 $events = get_option( $key, null ); 487 delete_option( $key ); 488 if ( is_array( $events ) ) { 489 koalab_sync_deliver( $events, (int) $attempt ); 490 } 491 } 492 add_action( 'koalab_sync_deliver_parked', 'koalab_sync_deliver_parked', 10, 2 ); 332 493 333 494 /** … … 387 548 } 388 549 } 550 // Kept for retries scheduled by 1.1.0/1.2.0 before the update, which carry the batch itself. 389 551 add_action( 'koalab_sync_deliver', 'koalab_sync_deliver', 10, 2 ); 390 552 … … 406 568 add_action( 'woocommerce_before_product_object_save', 'koalab_sync_capture_product_save', 20, 1 ); 407 569 add_action( 'woocommerce_before_product_variation_object_save', 'koalab_sync_capture_product_save', 20, 1 ); 570 571 // Stock written by `wc_update_product_stock()` outside an order: bulk edit, ERPs, other plugins. 572 add_action( 'woocommerce_product_before_set_stock', 'koalab_sync_capture_before_set_stock', 20, 1 ); 573 add_action( 'woocommerce_variation_before_set_stock', 'koalab_sync_capture_before_set_stock', 20, 1 ); 574 add_action( 'woocommerce_product_set_stock', 'koalab_sync_capture_set_stock', 20, 1 ); 575 add_action( 'woocommerce_variation_set_stock', 'koalab_sync_capture_set_stock', 20, 1 ); 408 576 409 577 // Order-driven stock: precise old→new + origin. … … 420 588 add_action( 'woocommerce_order_status_' . $status, $open_sale, 1, 1 ); 421 589 } 422 add_action( 'woocommerce_order_status_cancelled', function ( $order_id ) { 423 koalab_sync_ctx_open( 'return', $order_id ); 424 }, 1, 1 ); 590 // Every status in which WooCommerce gives a reduced order's units back 591 // (`wc_maybe_increase_stock_levels`): cancelled, pending and —since WooCommerce 11.0— failed. 592 foreach ( array( 'cancelled', 'pending', 'failed' ) as $status ) { 593 add_action( 'woocommerce_order_status_' . $status, function ( $order_id ) { 594 koalab_sync_ctx_open( 'return', $order_id ); 595 }, 1, 1 ); 596 } 425 597 add_action( 'woocommerce_create_refund', function () { 426 598 koalab_sync_ctx_open( 'refund', 0 ); -
koalab-sync/trunk/koalab-sync.php
r3629219 r3762744 1 1 <?php 2 2 /** 3 * Plugin Name: Koalab Sync — Bulk Price & Stock Updatesfor WooCommerce3 * Plugin Name: Koalab Sync for WooCommerce 4 4 * Plugin URI: https://koalab.tech 5 * Description: Update prices and stock of your entire WooCommerce catalog from your supplier's Excel/CSV price list — with markup rules, preview and one-click revert.6 * Version: 1. 1.05 * Description: Optional companion for Koalab Sync: sends your store's price and stock changes to Koalab in seconds, each with its cause. Koalab works without it, through WooCommerce's own webhooks. 6 * Version: 1.3.0 7 7 * Author: Koalab 8 8 * License: GPLv2 or later … … 13 13 * Requires Plugins: woocommerce 14 14 * WC requires at least: 7.0 15 * WC tested up to: 11.2 15 16 */ 16 17 … … 19 20 } 20 21 21 define( 'KOALAB_SYNC_VERSION', '1.1.0' ); 22 define( 'KOALAB_SYNC_APP_URL', 'https://koalab.tech' ); 22 define( 'KOALAB_SYNC_VERSION', '1.3.0' ); 23 24 /** 25 * A dónde habla el plugin. Producción por defecto, que es lo que instala un comerciante. 26 * 27 * Se puede fijar antes desde `wp-config.php` para apuntarlo a QA o a una API local. Sin esta 28 * guarda no se podía: el valor estaba escrito en duro, así que el plugin sólo sabía hablar con 29 * producción y no había forma de probarlo de punta a punta sin editar su código. Definirlo fuera 30 * además provocaba un aviso de PHP por redefinir una constante. 31 * 32 * No cambia nada para quien lo instala desde WordPress.org: si nadie lo define antes, vale lo mismo 33 * que valía. 34 */ 35 if ( ! defined( 'KOALAB_SYNC_APP_URL' ) ) { 36 define( 'KOALAB_SYNC_APP_URL', 'https://koalab.tech' ); 37 } 23 38 24 39 // Movement capture engine (P4) — optional store-side change tracking. 25 40 require_once plugin_dir_path( __FILE__ ) . 'includes/movements.php'; 41 // Heartbeat and «I am being switched off», while movement sync is connected (1.3.0). 42 require_once plugin_dir_path( __FILE__ ) . 'includes/signals.php'; 26 43 27 44 // Translations are loaded automatically by WordPress.org for plugins hosted there … … 42 59 register_activation_hook( __FILE__, function () { 43 60 update_option( 'koalab_sync_show_notice', 1 ); 44 } ); 61 koalab_sync_on_activate(); 62 } ); 63 64 /** 65 * On deactivation: tell Koalab, so it goes back to WooCommerce's own webhooks at once instead of waiting 66 * for a heartbeat that will not come. See includes/signals.php. 67 */ 68 register_deactivation_hook( __FILE__, 'koalab_sync_on_deactivate' ); 69 70 add_action( KOALAB_SYNC_HEARTBEAT_HOOK, 'koalab_sync_heartbeat' ); 71 add_action( 'init', 'koalab_sync_schedule_heartbeat' ); 72 add_action( 'admin_init', 'koalab_sync_heartbeat_fallback' ); 45 73 46 74 /** … … 81 109 'domain' => rawurlencode( home_url() ), 82 110 ), 83 KOALAB_SYNC_APP_URL . '/ apps/sync/credentials'111 KOALAB_SYNC_APP_URL . '/channels' 84 112 ); 85 113 } … … 214 242 'utm_source' => 'wp-plugin', 215 243 ), 216 KOALAB_SYNC_APP_URL . '/ apps/sync/credentials'244 KOALAB_SYNC_APP_URL . '/channels' 217 245 ); 218 246 // External redirect to the Koalab app: whitelist its host so wp_safe_redirect() allows it. … … 237 265 $redirect = add_query_arg( 'koalab_paired', $res['ok'] ? '1' : 'pending', $redirect ); 238 266 } elseif ( 'disconnect' === $action ) { 267 // Up to 1.2.0 Koalab never heard about this and kept ignoring WooCommerce's own webhooks. 268 koalab_sync_signal_off( 'disconnected' ); 239 269 koalab_sync_clear_connection(); 270 wp_clear_scheduled_hook( KOALAB_SYNC_HEARTBEAT_HOOK ); 240 271 delete_option( 'koalab_sync_pair_nonce' ); 241 272 $redirect = add_query_arg( 'koalab_disconnected', '1', $redirect ); … … 279 310 </h2> 280 311 <p style="font-size:14px;color:#50575e;max-width:620px;"> 281 <?php esc_html_e( ' Capture the price and stock changes that happen directly in your store — sales, returns, manual edits and third-party plugins — so they all show up in Koalab\'s Movements screen. Koalab keeps working without this; the plugin just adds deeper tracking.', 'koalab-sync' ); ?>312 <?php esc_html_e( 'Koalab keeps working without this: WooCommerce\'s own webhooks already tell it about sales and price and stock edits within minutes. With movement sync, every change made in your store arrives in seconds, labelled with its exact time and cause — sale, refund, hand edit, import or another plugin — and SKU, name, status and weight changes are recorded too.', 'koalab-sync' ); ?> 282 313 </p> 283 314 … … 309 340 <h3 style="font-size:1em;margin:0 0 8px;"><?php esc_html_e( 'Option A — Enter a code', 'koalab-sync' ); ?></h3> 310 341 <p style="font-size:13px;color:#50575e;margin:0 0 8px;"> 311 <?php esc_html_e( 'In Koalab → your store → Movements, generate a connection code and paste it here.', 'koalab-sync' ); ?>342 <?php esc_html_e( 'In Koalab, go to Channels → Configure → Plugin tab, generate a connection code and paste it here.', 'koalab-sync' ); ?> 312 343 </p> 313 344 <form method="post" action="<?php echo esc_url( admin_url( 'admin.php?page=koalab-sync' ) ); ?>"> -
koalab-sync/trunk/readme.txt
r3629219 r3762744 1 === Koalab Sync — Bulk Price & Stock Updatesfor WooCommerce ===1 === Koalab Sync for WooCommerce === 2 2 Contributors: koalabtech 3 Tags: bulk edit, prices, stock, woocommerce, excel3 Tags: multichannel, inventory sync, stock sync, price sync, woocommerce 4 4 Requires at least: 6.0 5 Tested up to: 7. 05 Tested up to: 7.1 6 6 Requires PHP: 7.4 7 Stable tag: 1. 1.07 Stable tag: 1.3.0 8 8 License: GPLv2 or later 9 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 10 10 11 Update prices and stock of your entire WooCommerce catalog from your supplier's Excel/CSV price list — with markup rules and one-click revert.11 Optional companion for Koalab Sync: sends your store's price and stock changes to Koalab in seconds, each with its cause. 12 12 13 13 == Description == 14 14 15 **Koalab Sync** connects your WooCommerce store to [Koalab](https://koalab.tech), a cloud tool that turns your supplier's price list into an updated catalog in minutes.15 [Koalab Sync](https://koalab.tech) is a cloud service that keeps your product catalog in one place and syncs prices, stock and sale prices to your sales channels — your WooCommerce store, and also Mercado Libre, Shopify or Tiendanube if you sell there. It can also update the whole catalog from a supplier's Excel/CSV price list, with markup rules, a preview and one-click revert. 16 16 17 If you run a store that resells products from distributors — hardware, auto parts, pharmacies, electronics, cosmetics — you know the routine: your supplier sends a new price list in Excel, and you spend hours updating products one by one, or fighting with CSV imports that break your catalog.17 **You do not need this plugin to use Koalab Sync.** You connect your store from Koalab with WooCommerce's own authorization screen, and Koalab subscribes to WooCommerce's webhooks: your sales and your price and stock edits reach Koalab within a few minutes, with nothing installed in WordPress. 18 18 19 Koalab Sync fixes that:19 This plugin is an optional companion for two cases: 20 20 21 * **Upload the Excel/CSV your supplier sends you** — no reformatting needed. Column names are detected automatically and products are matched by SKU or ID. 22 * **Apply your profit margin automatically** — markup, rounding and min/max price rules per store. 23 * **Preview every change before it happens** — see old price → new price for every product, with alerts for suspicious changes (price zero, sale price above regular price, sudden drops). 24 * **Revert with one click** — every update creates a snapshot. If something goes wrong, undo it entirely. 25 * **Schedule updates** — leave the update ready and let it run at night. 26 * **Zero load on your site** — everything runs in Koalab's cloud through the official WooCommerce REST API. This plugin adds no queries, no cron jobs and no tables to your WordPress. 21 * **You want the detail.** With movement sync connected, each change made in your store reaches Koalab within seconds of the request that made it, labeled with its exact time and its cause — sale, return, refund, hand edit, REST API, import or scheduled sale. Changes to SKU, name, status, stock management and weight are recorded too, not only price and stock. 22 * **WooCommerce's notices are not reaching Koalab.** WooCommerce sends its webhooks from its task queue, which depends on WP-Cron. If WP-Cron is disabled on your site, or a security plugin blocks outgoing requests, Koalab only learns about your store with its periodic import. The plugin delivers changes itself at the end of each request, without waiting for WP-Cron (on PHP-FPM hosts), and retries when Koalab cannot be reached. 27 23 28 **Free plan available** — connect 1 store and run up to 5 updates per month at no cost, no credit card required.24 While movement sync is connected, Koalab stops using WooCommerce's webhooks for that store, so no change is counted twice. If you deactivate, delete or disconnect the plugin, it tells Koalab, and Koalab goes back to WooCommerce's webhooks on its own. The plugin also checks in once a day, so Koalab notices when it has stopped reporting for any other reason. 29 25 30 = What th is plugin does =26 = What the plugin adds to your WordPress = 31 27 32 This plugin adds a page under your WooCommerce menu that connects your store to Koalab using WooCommerce's official authorization flow (`wc-auth`). You approve access with one click — no copying API keys, and Koalab never sees your WordPress password. All the heavy lifting happens in the cloud. 28 * A page under **WooCommerce → Koalab Sync** with links to create your Koalab account or connect your store (through WooCommerce's official `wc-auth` authorization flow: you approve access with one click, and Koalab never sees your WordPress password). 29 * The optional movement sync described above, off until you pair the plugin with your store in Koalab. 30 31 The plugin adds no database tables and does not write to your products: everything Koalab writes to your store goes through the WooCommerce REST API keys you authorized. 33 32 34 33 = Disponible en español = 35 34 36 Koalab está completamente disponible en español (y portugués). Sube la lista de precios de tu proveedor en Excel, aplica tu margen de ganancia automáticamente, revisa los cambios antes de aplicarlos y actualiza cientos de productos en minutos — con reversión de un clic si algo sale mal. Plan gratuito disponible: 1 tienda y 5 actualizaciones al mes, sin tarjeta de crédito.35 Koalab Sync está disponible en español y portugués. Este plugin es opcional: sin él, los avisos de la propia WooCommerce ya traen a Koalab tus ventas y tus cambios de precio y stock en unos minutos. Con él llegan en segundos, con su hora exacta y su causa, y funciona también en tiendas donde esos avisos no salen. 37 36 38 37 == External services == … … 43 42 44 43 * **When you connect your store** (clicking the connect button): you are redirected to Koalab and your store's site URL (domain) is sent so Koalab can pair with your store. Access to your products is then granted through WooCommerce's official authorization flow (`wc-auth`), which issues REST API keys you explicitly approve. 45 * **During synchronization** (performed by Koalab, not by this plugin): Koalab reads and writes your WooCommerce **products** (names, SKUs, prices, stock) through those authorized REST API keys. Koalab does not access your WordPress admin, customers or orders. 46 * **Optional movement tracking** (off unless you enable it): when a product's price or stock changes in your store, the plugin sends the change event (product ID, SKU, changed field, old/new value and timestamp) to Koalab's API so it appears on Koalab's Movements screen. 44 * **During synchronization** (performed by Koalab, not by this plugin): Koalab reads and writes your WooCommerce **products** (names, SKUs, prices, stock) through those authorized REST API keys, and reads your store's status report to know your currency, your WooCommerce version and whether this plugin is active. 45 * **Store notifications** (performed by Koalab, not by this plugin): unless movement sync is connected and reporting, Koalab subscribes your store to WooCommerce's own webhooks for product updates, product deletions and orders, so it learns about sales and edits without waiting for its periodic import. From an order Koalab keeps only its number, status, dates and the products and quantities sold — never the buyer's name, address or contact details. Koalab does not access your WordPress admin. 46 * **Optional movement sync** (off unless you pair the plugin with your store in Koalab): when a product's price, stock, SKU, name, status, stock management or weight changes in your store, the plugin sends the change event (product ID, SKU, product name, changed field, old and new value, cause, order number when there is one, and timestamp) to Koalab's API so it appears on Koalab's Movements screen. 47 * **Connection check** (only while movement sync is connected): once a day, and when the plugin is deactivated, deleted or disconnected from its page, the plugin sends your Koalab store ID, your site URL, the plugin version and a timestamp, so Koalab knows whether the plugin is still reporting and can go back to WooCommerce's webhooks when it is not. No product, order or customer data is included. 48 49 Every request the plugin makes to Koalab is signed with a secret shared only when you pair the plugin. 47 50 48 51 This service is provided by Koalab. By connecting your store you agree to Koalab's Terms of Service (https://koalab.tech/terms) and Privacy Policy (https://koalab.tech/privacy). … … 50 53 == Installation == 51 54 55 You do not need the plugin to connect your store: in Koalab, go to **Channels**, connect a channel and choose WooCommerce. Install the plugin only if you want movement sync: 56 52 57 1. Install and activate the plugin from the WordPress plugin directory. 53 2. Go to **WooCommerce → Koalab Sync**. 54 3. Click **"Create free account & connect"** (or "I already have an account"). 55 4. Approve access on the WooCommerce authorization screen — that's it. 58 2. In Koalab, go to **Channels → Configure** on your WooCommerce store, open the **Plugin** tab and generate a connection code. 59 3. In WordPress, go to **WooCommerce → Koalab Sync**, paste the code under **Movement sync (optional)** and click **Connect** — or click **Open Koalab** and confirm the store there. 56 60 57 61 Requirements: WooCommerce 7.0+, your store must be served over HTTPS, and pretty permalinks must be enabled (Settings → Permalinks). … … 59 63 == Frequently Asked Questions == 60 64 61 = Is it free? =65 = Do I need this plugin to use Koalab Sync? = 62 66 63 Yes — the free plan includes 1 store and 5 bulk updates per month, with no credit card. Paid plans add more updates, scheduled syncs, price rules and longer history. 67 No. Without it, WooCommerce's own webhooks already bring your sales and price and stock edits to Koalab within minutes. The plugin makes them arrive in seconds, labels each change with its exact time and cause, records SKU, name, status and weight changes too, and keeps working where WooCommerce's webhooks do not go out. 68 69 = What happens if I deactivate or delete the plugin? = 70 71 The plugin tells Koalab, and Koalab subscribes your store to WooCommerce's own webhooks again: your sales keep arriving, within minutes instead of seconds. If you activate it again, Koalab uses it again on its own. If the plugin stops reporting without saying so (for example, it was removed over FTP), Koalab notices when its daily check-in stops arriving and does the same. 72 73 = Is Koalab Sync free? = 74 75 There is a free plan, with no credit card. Paid plans add more updates, more channels, scheduled syncs and price rules. See https://koalab.tech for the current plans. 64 76 65 77 = Does this plugin slow down my store? = 66 78 67 No. The plugin only adds an admin page with a connect button. Synchronization runs in Koalab's cloud and talks to your store through the standard WooCommerce REST API, the same one used by mobile apps and integrations.79 No. Without movement sync the plugin only adds an admin page. With it, the plugin notes which fields changed when a product is saved and sends them once, after the page has been served, plus one small check-in a day. Synchronization runs in Koalab's cloud and talks to your store through the standard WooCommerce REST API. 68 80 69 81 = What data does Koalab access? = 70 82 71 Only your WooCommerce products (names, SKUs, prices, stock), through the API keys you explicitly authorize. Credentials are stored encrypted (AES-256). Koalab never accesses your WordPress admin, customers or orders.83 Your WooCommerce products (names, SKUs, prices, stock) and your store's status report, through the API keys you explicitly authorize, and — through WooCommerce's webhooks — the status and the lines (products and quantities) of your orders, so a sale reaches your other channels. Nothing about the buyer is kept. Credentials are stored encrypted (AES-256). Koalab never accesses your WordPress admin. 72 84 73 85 = Does it work with product variations? = 74 86 75 Yes — variations are matched and updated just like simple products.87 Yes — variations are captured and synced just like simple products. 76 88 77 = Can I u ndo an update? =89 = Can I update my catalog from a supplier's price list? = 78 90 79 Yes. Every update creates a snapshot of the previous values. You can revert the entire update with one click from the history. 80 81 = My supplier's Excel has different column names. Will it work? = 82 83 Yes. Koalab detects common column names automatically (SKU, price, stock, etc. — in English, Spanish and Portuguese) and lets you map any column manually. 91 Yes, in Koalab: upload the Excel or CSV your supplier sends you, map its columns, apply your markup rules, preview every change and revert it with one click. That runs in Koalab, with or without this plugin. 84 92 85 93 == Screenshots == … … 91 99 92 100 == Changelog == 101 102 = 1.3.0 = 103 * New: while movement sync is connected, the plugin checks in with Koalab once a day and tells it when it is deactivated, deleted or disconnected from its page. Koalab then goes back to WooCommerce's own webhooks instead of waiting for movements that will not come. Up to 1.2.0, a plugin switched off without disconnecting it left Koalab unaware of the store's changes until its next catalog import. 104 * The description now says what the plugin is today: an optional companion of Koalab Sync. Koalab works without it. 105 * New: movement sync reports when a product stops (or starts again) tracking inventory, so Koalab marks it as "no inventory control" instead of reading it as sold out. 106 * Fixed: stock changes made with WooCommerce's bulk edit, when editing an order's items in the admin, or by ERPs and stock plugins that use `wc_update_product_stock()` were not captured. 107 * Fixed: a refund that restocks items now carries its order number, and a refund with several items is no longer reported twice. 108 * Fixed: an order moved back to pending, or a held order whose payment fails (WooCommerce 11.0+), is reported as a return. 109 * Fixed: the retry of a large batch was never scheduled, so those changes were lost if Koalab could not be reached. 110 * The setup text points at Koalab's current screens (Channels → Configure → Plugin tab). 111 * Tested with WordPress 7.1 and WooCommerce 11.2. 112 113 = 1.2.0 = 114 * Movement sync now also captures SKU, name, status, stock management and weight changes — not just prices and stock. 115 * Fixed: "Open Koalab" and the connect link pointed at a page that no longer exists, so they landed on a 404. Both now open the Channels screen. 116 * Fixed: the setup text sent you to Movements to generate the connection code. It is generated in Channels → your store → Plugin. 117 * Tested with WordPress 7.1. 93 118 94 119 = 1.1.0 = … … 102 127 == Upgrade Notice == 103 128 129 = 1.3.0 = 130 Koalab now notices when the plugin is deactivated or stops reporting. Also captures stock changes from bulk edits and other plugins, reports "no inventory control", and no longer loses retries of large batches. 131 132 = 1.2.0 = 133 Captures more product changes, and fixes the two connection links that led to a missing page. 134 104 135 = 1.1.0 = 105 136 Adds optional movement sync (sales, returns and manual edits) plus low-stock alerts. -
koalab-sync/trunk/uninstall.php
r3629219 r3762744 12 12 } 13 13 14 // Tell Koalab before the secret goes (1.3.0): the connection ends here, and Koalab goes back to 15 // WooCommerce's own webhooks. Nothing is sent if movement sync was never connected. 16 require_once __DIR__ . '/includes/signals.php'; 17 koalab_sync_signal_off( 'uninstalled' ); 18 wp_clear_scheduled_hook( KOALAB_SYNC_HEARTBEAT_HOOK ); 19 14 20 $koalab_sync_options = array( 15 21 'koalab_sync_show_notice', … … 21 27 'koalab_sync_pair_nonce', 22 28 'koalab_sync_last_error', 29 'koalab_sync_last_beat', 23 30 ); 24 31 … … 26 33 delete_option( $koalab_sync_option ); 27 34 } 35 36 // Batches parked for a retry (1.3.0+), and their scheduled actions. 37 global $wpdb; 38 $koalab_sync_parked = $wpdb->get_col( $wpdb->prepare( "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $wpdb->esc_like( 'koalab_sync_retry_' ) . '%' ) ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery 39 foreach ( $koalab_sync_parked as $koalab_sync_option ) { 40 delete_option( $koalab_sync_option ); 41 } 42 if ( function_exists( 'as_unschedule_all_actions' ) ) { 43 as_unschedule_all_actions( 'koalab_sync_deliver_parked' ); 44 as_unschedule_all_actions( 'koalab_sync_deliver' ); 45 }
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)