Skip to content

3.6.0

Choose a tag to compare

@PaulArgoud PaulArgoud released this 18 Feb 23:24
· 37 commits to main since this release

Added

  • WP ERP Accounting module — Completes the WP ERP triptyque (HR + CRM + Accounting). Syncs journal entries → account.move (bidirectional), chart of accounts → account.account (bidirectional), journals → account.journal (bidirectional). Custom table access (erp_acct_journals, erp_acct_ledger_details, erp_acct_chart_of_accounts). Invoice status mapping (draft/awaiting_payment/paid/overdue/void → Odoo states). Hooks: erp_acct_new_journal, erp_acct_new_invoice, erp_acct_update_invoice, erp_acct_new_bill, erp_acct_new_expense
  • LearnPress module — LMS module for LearnPress 4.0+ (100k+ installations). Extends LMS_Module_Base. Syncs courses → product.product (bidirectional), orders → account.move (push, auto-post), enrollments → sale.order (push, synthetic ID encoding). Translation support for courses. Hooks: save_post_lp_course, learn-press/order/status-completed, learn-press/user/course-enrolled
  • Food Ordering module — Aggregate module for GloriaFood + WPPizza → Odoo POS Restaurant. Push-only, syncs food orders → pos.order with pos.order.line One2many tuples. Strategy-based extraction via Food_Order_Extractor. Per-plugin detection and setting toggles. Hooks: save_post_flavor_order, wppizza_order_complete
  • Survey & Quiz module — Aggregate module for Quiz Maker (Ays) + Quiz And Survey Master → Odoo Survey. Push-only, syncs quizzes → survey.survey with question_and_page_ids One2many, responses → survey.user_input with user_input_line_ids One2many. Strategy-based extraction via Survey_Extractor. Question type mapping (radio→simple_choice, checkbox→multiple_choice, etc.)
  • myCRED module — Points & rewards module for myCRED 2.0+ (10k+ installations). Syncs point balances → loyalty.card (bidirectional via Loyalty_Card_Resolver), badge types → product.template (push-only, service products). Anti-loop via odoo_sync reference guard. Configurable Odoo loyalty program ID. Hooks: mycred_update_user_balance, mycred_after_badge_assign
  • Jeero Configurator module — Product configurator module for Jeero WC Product Configurator. Push-only, syncs configurable products → mrp.bom with bom_line_ids One2many tuples. Cross-module entity_map resolution for WooCommerce product → Odoo product.template ID. Transient error pattern for unsynced component dependencies. Requires WooCommerce module. Hooks: save_post_product (filtered for configurable products)
  • Documents module — Bidirectional document sharing between WordPress and Odoo Documents (Enterprise). Supports WP Document Revisions (document CPT) and WP Download Manager (wpdmpro CPT). Syncs documents → documents.document (bidirectional, base64 file encoding, SHA-256 change detection), folders → documents.folder (bidirectional, hierarchy via parent_folder_id). Hooks: save_post_document, save_post_wpdmpro, before_delete_post, created_document_category, edited_document_category
  • Odoo_Model enum additions — 8 new cases: AccountJournal, AccountAccount, PosOrder, PosOrderLine, SurveySurvey, SurveyUserInput, DocumentsDocument, DocumentsFolder
  • WC Inventory module — Advanced multi-warehouse stock management with optional ATUM Multi-Inventory integration. Syncs warehouses (stock.warehouse, pull-only), stock locations (stock.location, pull-only), and stock movements (stock.move, bidirectional). Complements the WooCommerce module's global stock.quant sync with individual move tracking. Hooks at priority 20 on woocommerce_product_set_stock (after WC module at 10). ATUM detection at runtime for multi-location support
  • WC Shipping module — Bidirectional shipment tracking sync with optional ShipStation, Sendcloud, Packlink, and AST integration. Pushes WC tracking data to Odoo stock.picking (carrier_tracking_ref), pulls Odoo shipment tracking back to WC order meta (AST-compatible format). Optionally syncs WC shipping methods as delivery.carrier records. Provider-specific extraction methods for each shipping plugin
  • WC Returns module — Full return/refund lifecycle with optional YITH WooCommerce Return & Warranty and ReturnGO integration. Pushes WC refunds as Odoo credit notes (account.move with move_type=out_refund), pulls credit notes back as WC refunds. Optionally creates return stock.picking entries. Auto-posts credit notes via Odoo_Accounting_Formatter::auto_post(). Cross-module entity resolution for original invoice (reversed_entry_id)
  • Odoo_Accounting_Formatter::for_credit_note() — New static method for formatting credit note data (out_refund account.move). Reuses build_invoice_lines() for line items, supports optional reversed_entry_id for linking to original invoice. Filterable via wp4odoo_credit_note_data hook
  • Odoo_Model enum additions — 5 new cases: StockWarehouse, StockLocation, StockMove, StockQuant, StockPickingType

Added (Architecture)

  • GamiPress/myCRED gamification exclusive group — GamiPress and myCRED both target loyalty.card via Loyalty_Card_Resolver with (partner_id, program_id). New gamification exclusive group prevents both from running simultaneously (first-registered wins: GamiPress before myCRED)
  • Lazy loading Module_Registry — Disabled modules are no longer instantiated at boot. register_all() stores disabled module class names in a $deferred map; get() materializes on demand, all() materializes everything (for admin UI). Reduces memory footprint on sites with many detected but disabled modules
  • Module health manifest — New tests/module-health-manifest.php declares all third-party symbols (classes, functions, constants) per module. ModuleHealthManifestTest validates that stubs match the manifest and that the manifest covers all registered modules. 196 data-driven tests catch stub drift when upstream plugins evolve
  • Sync flow integration tests — 5 new integration tests (tests/Integration/SyncFlowTest.php) covering the full push/pull pipeline: hook → queue → process → Odoo transport → entity_map. Uses SyncFlowTransport mock for deterministic transport responses
  • Per-module circuit breaker — New Module_Circuit_Breaker class isolates failing modules without blocking the entire sync. Dual-level design: existing global Circuit_Breaker handles transport failures (Odoo down), new module breaker handles per-module failures (model uninstalled, access rights). Threshold: 5 consecutive batches with ≥80% failure ratio. Recovery delay: 600s (half-open probe). State stored in wp4odoo_module_cb_states option. Integrated into Sync_Engine (per-module outcome tracking), Failure_Notifier (per-module email with cooldown), and health dashboard (open modules display). Auto-cleans stale state older than 2 hours
  • Entity_Map orphan cleanup — New Entity_Map_Repository::cleanup_orphans() method detects and removes entity_map entries where the WP post no longer exists (LEFT JOIN against wp_posts). Excludes user-based modules (BuddyBoss, FluentCRM, etc.). New WP-CLI command: wp wp4odoo cleanup orphans [--module=<module>] [--dry-run] [--yes]
  • Trait extraction refactoring — 6 new traits extracted from 3 large classes to improve separation of concerns:
    • Hook_Lifecycle (from Module_Base): $registered_hooks, safe_callback(), register_hook(), teardown()
    • Translation_Accumulator (from Module_Base): $translation_buffer, get_translatable_fields(), accumulate_pull_translation(), flush_pull_translations()
    • Sync_Job_Tracking (from Sync_Engine): $batch_failures, $batch_successes, $module_outcomes, handle_failure(), record_module_outcome()
    • Failure_Tracking_Settings (from Settings_Repository): consecutive failure count and last failure email timestamp
    • UI_State_Settings (from Settings_Repository): onboarding/checklist dismissed flags, webhook confirmation, cron health
    • Network_Settings (from Settings_Repository): multisite network connection, site → company_id mapping

Fixed (Architecture)

  • LRU cache eviction ratioEntity_Map_Repository::evict_cache() now keeps 75% of entries during eviction (was 50%). Prevents counter-productive cache thrashing during batch operations where frequent eviction caused redundant DB queries
  • Stale job recoverySync_Queue_Repository::recover_stale_processing() no longer increments attempts when recovering interrupted jobs. A stale-processing job was interrupted (crash/timeout), not genuinely failed — incrementing attempts caused premature failure at max_attempts
  • Circuit breaker TTLCircuit_Breaker transient and DB state TTL now uses RECOVERY_DELAY × 2 (600s) instead of HOUR_IN_SECONDS (3600s). During long Odoo outages (>1h), the old state expired and the circuit re-closed, sending a burst of requests to a still-down server. Stale DB state discard increased from 1h to 2h
  • Transactional migrationsDatabase_Migration::run_migrations() now wraps each migration in a START TRANSACTION / COMMIT block with ROLLBACK on failure, preventing partial schema changes from leaving tables in an inconsistent state
  • Dual accounting entity type validationDual_Accounting_Module_Base::load_wp_data() now validates $entity_type against the known parent/child types before processing. Invalid entity types are logged and return empty data instead of silently falling through
  • Batch create intra-group dedupBatch_Create_Processor::group_eligible_jobs() now deduplicates by wp_id within each module:entity_type group. Prevents creating duplicate Odoo records when two create jobs for the same WP entity are in the same batch
  • JSON decode error classificationBatch_Create_Processor now classifies invalid JSON payloads as Error_Type::Permanent (was Transient). A corrupted payload will never self-fix, so retrying wastes 2 attempts
  • Session detection robustnessOdoo_Client::is_session_error() now detects HTTP 401 (Odoo 17+), JSON-RPC error code 100 (session invalid), and additional keywords (session invalid, authentication failed). Reduces risk of silent re-auth failure when Odoo changes error messages
  • Non-idempotent method detectionOdoo_Client session retry now skips all non-idempotent methods (create, action_*, button_*, validate), not just create. Prevents unintended side effects from retrying workflow actions
  • Webhook HMAC loggingWebhook_Handler now logs a debug message when a webhook is received without HMAC signature (token-only auth), surfacing the weaker auth mode to site admins
  • Booking service sync failure propagationBooking_Module_Base::push_to_odoo() now aborts with a Transient failure if ensure_service_synced() fails, instead of pushing a booking with an invalid Odoo service reference
  • DNS timeout for SSRFAdmin_Ajax::sanitize_url() now applies a 5-second default_socket_timeout during DNS resolution, preventing malicious hostnames with slow DNS from blocking the PHP thread indefinitely
  • Module_Registry materialization loggingModule_Registry::materialize() now catches \Throwable and logs the error instead of silently swallowing it. The module remains absent (callers handle null), but the failure is visible in logs
  • Settings cache invalidation — New Settings_Repository::flush_cache() method forces re-read from wp_options after external modifications (WP-CLI, concurrent cron, or another process)
  • Module circuit breaker concurrencyModule_Circuit_Breaker::record_module_failure() now uses advisory lock (GET_LOCK('wp4odoo_mcb_failure')) to prevent lost-update races on the shared wp_options JSON blob. is_module_available() now uses per-module probe mutex (transient + advisory lock double-check) to prevent multiple concurrent probe batches in half-open state. Probe transients cleaned in record_module_success() and reset_module()
  • company_id injection loggingSync_Orchestrator::maybe_inject_company_id() now logs a warning when get_company_id() throws, instead of silently swallowing the exception
  • Locale mapping edge caseTranslation_Service::wp_to_odoo_locale() returns 'en_US' for empty or single-character inputs instead of producing an invalid '_' locale
  • Documents delete result checkDocuments_Module::delete_wp_data() simplified wp_delete_term() result check to true === $result (PHPStan: is_int() was unreachable for bool|WP_Error return type)

Fixed (Documentation)

  • ARCHITECTURE.md exclusive group priorities — Replaced ~15 stale exclusive_priority references (removed as dead code in v3.5.0) with accurate "first-registered wins" descriptions and actual registration order from Module_Registry::register_all()

Tests

  • 5 025 unit tests (7 653 assertions) — new tests covering Module_Circuit_Breaker (20 tests), Entity_Map_Repository::cleanup_orphans() (6 tests), audit fixes (12 tests: LRU eviction 75%, stale recovery no-increment, circuit breaker 2h TTL, batch create dedup, JSON permanent error, session detection 401/100, non-idempotent method guard, settings cache flush)

i18n

  • Regenerated .pot from all source files (931 → 944 msgid), translated 12 new strings in FR and ES (module circuit breaker, orphan cleanup CLI), recompiled .mo — 944 translated strings (was 931)