Skip to content

3.9.0

Choose a tag to compare

@PaulArgoud PaulArgoud released this 19 Feb 20:02
· 13 commits to main since this release

Fixed (Architecture)

  • safe_callback() preserves filter chainHook_Lifecycle::safe_callback() now returns the callback's result (was void). On crash, returns $args[0] so filter chains are not broken by a crashed callback
  • Module materialization failure UXModule_Registry::materialize() now records a version_warning entry when a module's constructor throws, making the failure visible in the admin health dashboard instead of being silently swallowed
  • Advisory Lock reentrancy guardAdvisory_Lock::acquire() returns true immediately if the lock is already held by the current instance, preventing redundant GET_LOCK() calls
  • Advisory Lock destructor safetyAdvisory_Lock::__destruct() releases the lock if still held (safety net for uncaught exceptions), wrapped in try-catch so destructors never throw
  • many2one_to_id rejects zero/negative IDsField_Mapper::many2one_to_id() now returns null for array-path IDs ≤ 0, preventing invalid Odoo references from propagating
  • Logger error_log fallbackLogger::flush_buffer() writes error/critical entries to PHP error_log() when $wpdb is unavailable (late shutdown), preventing silent log loss
  • cached_company_id multisite invalidationSync_Orchestrator::maybe_inject_company_id() now compares blog_id against the cached value, auto-invalidating the company_id cache when switch_to_blog() changes context
  • CLI try/finally on switch_to_blogCLI::sync(), reconcile(), and cleanup() now wrap their body in try/finally to guarantee restore_current_blog() is called even on exceptions
  • Microsecond date parsingField_Mapper::odoo_date_to_wp() now tries Y-m-d H:i:s.u format first, supporting Odoo timestamps with microsecond precision
  • Reconciliation batch size filterReconciler::reconcile() batch size is now filterable via wp4odoo_reconcile_batch_size, with ≤ 0 guard falling back to the default (200)
  • Translation cache flush — New Translation_Service::flush_caches() static method clears ir.translation and active languages transients. Called from CLI cache flush command
  • Filter return type validationmap_to_odoo() and pull_from_odoo() now validate that apply_filters() returns an array, falling back to the pre-filter value if a third-party filter returns an unexpected type
  • Stale recovery clears processed_atSync_Queue_Repository::recover_stale_processing() now resets processed_at to NULL when recovering jobs to 'pending', restoring the semantic contract (NULL = not yet processed) and preventing phantom timestamps in monitoring dashboards
  • Recovery transaction try-finallySync_Queue_Repository::recover_stale_processing() now wraps the SAVEPOINT/RELEASE block in try/finally, guaranteeing the transaction is committed even if a query throws
  • Translation buffer flush loss protectionTranslation_Accumulator::flush_pull_translations() now uses per-model try-catch. Failed models retain their buffer entries for the next flush instead of being silently discarded
  • Oversized payload observabilitySync_Queue_Repository::enqueue() now fires wp4odoo_enqueue_rejected action when a payload exceeds 1 MB, providing a hook for logging/monitoring instead of failing silently
  • WooCommerce safe_callback consistencywp4odoo_batch_processed hook in WooCommerce module now wrapped in safe_callback() for consistency with all other hook registrations
  • Compatibility report URL constructionAdmin::build_compat_report_url() now places query parameters before the #forms anchor. Previously parameters were appended after the fragment, making them invisible to the WPForms URL prefill parser
  • Compatibility report module field — Updated WPForms field ID from _1 (dropdown) to _14 (text input) for the module name, allowing any module name to be prefilled without dropdown restrictions

Fixed (Multisite)

  • Network-wide deactivation cron cleanupdeactivate() now iterates over all sites via get_sites() when network-deactivated, preventing orphaned cron hooks on subsites (mirrors existing activate() pattern)

Changed (Core)

  • Schema cache TTL reduced to 4 hoursSchema_Cache::CACHE_TTL changed from DAY_IN_SECONDS (24 h) to 4 * HOUR_IN_SECONDS. Odoo schema changes (new fields, renamed models) are now picked up within 4 hours instead of 24
  • Stats cache invalidation moved to batch level — Removed per-enqueue() call to invalidate_stats_cache() (was causing transient thrashing on high-throughput sites). Stats cache is now only invalidated after batch processing completes in Sync_Engine::run_with_lock(), where it was already called
  • Exclusive group admin warningModule_Registry::register() now records a version_warnings entry when a module is blocked by an exclusive group, identifying the active module and group name (e.g. "Sales was not started because woocommerce is already active in the ecommerce group")
  • safe_callback() crash counterHook_Lifecycle now tracks a static $crash_count incremented on each caught \Throwable. Exposed via get_crash_count() for the health dashboard, making silent graceful degradation events visible to administrators
  • Schema_Cache::flush() in test teardownModule_Test_Case::reset_static_caches() now calls Schema_Cache::flush() to prevent cross-test schema cache leakage
  • Partner_Service static reset in test teardown — New Partner_Helpers::reset_partner_service() method, called from Module_Test_Case::reset_static_caches() to prevent cross-test partner service leakage

Tests

  • Added Logger::flush_buffer() calls in MultisiteScopingTest integration tests to account for deferred buffer writes