You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
WC Bookings silent push failure — Entity type 'product' was not declared in WC_Bookings_Module's $odoo_models (only 'service' and 'booking'), causing every booking product push to silently fail. Changed to 'service'
Exclusive group mismatch — WooCommerce, Sales, and EDD modules used 'commerce' as their exclusive group while ARCHITECTURE.md documented 'ecommerce'. Unified to 'ecommerce'
Batch creates double failure — Sync_Engine::process_batch_creates() added jobs to $claimed_jobs before JSON validation, causing handle_failure() to be called twice (once for invalid JSON, once for batch error). Moved append after validation
SSRF bypass via DNS failure — is_safe_url() returned true when gethostbyname() failed (returns the input on DNS failure), allowing URLs with unresolvable hostnames to bypass SSRF protection. Now returns false
Queue health metrics cache leak — invalidate_stats_cache() cleared wp4odoo_queue_stats but not wp4odoo_queue_health, leaving stale health metrics
Stale recovery ordering — recover_stale_processing() ran after fetch_pending(), so freshly recovered jobs were excluded from the current batch. Reordered to recover first
Odoo_Client retry missing action — Retry path after session re-auth did not fire wp4odoo_api_call action, making retry calls invisible to monitors
MySQL 5.7 compat — @@in_transaction session variable query could produce a visible error on MySQL 5.7 (which lacks this variable). Wrapped with suppress_errors()
Dual accounting delete — resolve_accounting_model() was skipped for delete actions, causing delete calls to target the wrong Odoo model when OCA donation.donation was active
Helpdesk exclusive group/priority — Helpdesk_Module_Base used method overrides instead of properties for $exclusive_group and $exclusive_priority, inconsistent with all other intermediate bases. Converted to properties
Undefined $jobs variable — Sync_Engine::process_queue() could reference undefined $jobs if the try block threw before assignment
Partner email normalization — Partner_Service::get_or_create_batch() now trims and lowercases emails before Odoo lookup, preventing duplicate partners from case mismatches
Reconciler client hoisting — Reconciler resolved the Odoo client inside the per-entity loop instead of once before it
Logger context truncation — truncate_context() JSON-encoded the full array then truncated the string, producing invalid JSON. Now truncates the array first, then encodes
Empty encryption key warning — Odoo_Auth now logs a warning via error_log() when the encryption key is empty, aiding diagnosis of misconfigured installations
CLI --format validation — queue stats and queue list subcommands now reject unsupported --format values with a clear error
Ecwid cron orphan on deactivation — wp4odoo_ecwid_poll cron event was cleared on uninstall but not on plugin deactivation, leaving an orphaned cron entry. Added to deactivate()
Exclusive group priority documentation — ARCHITECTURE.md listed membership, invoicing, and helpdesk exclusive group priorities in reverse order (lower number shown as winning). Corrected to reflect actual >= logic where highest number wins
Added
Bidirectional WC stock sync — Stock push (WC → Odoo) via new Stock_Handler class. Version-adaptive API: stock.quant + action_apply_inventory() for Odoo 16+, stock.change.product.qty wizard for v14-15. Hooks: woocommerce_product_set_stock, woocommerce_variation_set_stock. Anti-loop guard prevents re-enqueue during pull
Compatibility report link — TESTED_UP_TO version warnings now include a "Report compatibility" link that opens a pre-filled WPForms form with module name, WP4Odoo version, third-party plugin version, WordPress version, PHP version, and Odoo major version. Shown in both the global admin notice banner and per-module notices on the Modules tab. Filterable via wp4odoo_compat_report_url
Odoo version detection — Transport interface gains get_server_version(): ?string. JSON-RPC extracts server_version from the authenticate response; XML-RPC calls version() on /xmlrpc/2/common after auth. test_connection() now populates the version field. The AJAX handler stores the version in wp4odoo_odoo_version option for use in compat reports and diagnostics
Gallery images sync — Image_Handler now supports product gallery images (product_image_ids ↔ _product_image_gallery). import_gallery() pulls Odoo product.image records with per-slot SHA-256 hash tracking and orphan cleanup. export_gallery() builds One2many [0, 0, {...}] tuples for push. Integrated into WC_Pull_Coordinator and WooCommerce_Module
Health dashboard tab — New "Health" tab in admin settings showing system status at a glance: active modules, pending queue depth, average latency, success rate, circuit breaker state, next cron run, cron warnings, compatibility warnings, and queue depth by module
Translatable fields for 4 modules — EDD, Events Calendar, LearnDash, and Job Manager modules now override get_translatable_fields(), enabling automatic WPML/Polylang translation pull for their primary content fields
Circuit breaker email notification — Failure_Notifier sends an email to the site admin when the circuit breaker opens, with failure count and a link to the health dashboard. Respects the existing cooldown interval
WooCommerce tax mapping — Configurable WC tax class → Odoo account.tax mapping via key-value settings. Applied per order line during push as tax_id Many2many tuples. AJAX endpoint fetches available Odoo taxes
WooCommerce shipping mapping — Configurable WC shipping method → Odoo delivery.carrier mapping via key-value settings. Sets carrier_id on sale.order during push. AJAX endpoint fetches available Odoo carriers
Separate gallery images setting — New sync_gallery_images checkbox (default on) controls gallery image push/pull independently of the featured image setting sync_product_images
Changed
push_entity() simplified — Removed redundant $module parameter from Module_Helpers::push_entity(). All 29 callsites across 19 trait files now use $this->id automatically
Circuit breaker constant public — Circuit_Breaker::OPT_CB_STATE made public; Settings_Page health tab references the constant instead of a hardcoded string
Form_Handler extract_normalised() — Extracted shared field iteration pipeline into a generic extract_normalised() method. Formidable, Forminator, and WPForms extractors now delegate to it; Gravity Forms uses empty_lead() instead of inline init
Options autoload optimization — Disabled autoload (false) on ~80 options that are only read during cron, admin, or sync operations: module settings, module mappings, webhook token, failure tracking, onboarding state, circuit breaker state, and Odoo version. Core options (connection, sync settings, log settings, module enabled flags, DB version) remain autoloaded
Polling safety limit warning — Entity_Map_Repository::get_module_entity_mappings() and Bookly_Handler batch queries now log a warning when the 50,000-row safety cap is reached, alerting administrators that some entities may be excluded from sync
PHPStan level 6 — Raised static analysis from level 5 to level 6 (adds missing typehint enforcement). Global missingType.iterableValue suppression for WordPress API conformance
Log module filter — Expanded the log viewer module dropdown from ~20 hardcoded entries to all 33 sync modules plus 5 system modules, organized in <optgroup> sections
Log level i18n — Log level labels (Debug, Info, Warning, Error, Critical) in the sync settings tab are now translatable
Admin JS i18n — Hardcoded English strings in admin.js (server error, unknown error, completed, remove) replaced with localized strings via wp_localize_script
XSS defense-in-depth — Added escapeHtml() helper in admin.js for log module and level fields in the AJAX log table