Skip to content

Commit 0d82a7d

Browse files
committed
3.9.1
1 parent 079961c commit 0d82a7d

File tree

2 files changed

+88
-21
lines changed

2 files changed

+88
-21
lines changed

ARCHITECTURE.md

Lines changed: 81 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ WordPress For Odoo/
138138
│ │ ├── class-wc-bookings-module.php # WC Bookings: extends Booking_Module_Base (uses WC_Bookings_Hooks trait)
139139
│ │ │
140140
│ │ ├── # ─── LMS (LearnDash + LifterLMS + TutorLMS + LearnPress + Sensei) ──
141-
│ │ ├── class-lms-module-base.php # LMS shared: enrollment loading pipeline (synthetic ID → partner → product → sale order)
141+
│ │ ├── class-lms-module-base.php # LMS shared: enrollment loading pipeline (synthetic ID → partner → product → sale order), shared load_enrollment_data()
142+
│ │ ├── class-lms-handler-base.php # LMS shared: abstract handler base (load_course template, build_invoice, build_sale_order)
142143
│ │ ├── trait-learndash-hooks.php # LearnDash: hook callbacks (course/group save, transaction, enrollment)
143144
│ │ ├── class-learndash-handler.php # LearnDash: course/group/transaction/enrollment data load
144145
│ │ ├── class-learndash-module.php # LearnDash: push sync coordinator (extends LMS_Module_Base, uses LearnDash_Hooks trait)
@@ -245,6 +246,19 @@ WordPress For Odoo/
245246
│ │ ├── class-fluent-support-handler.php # FS: ticket load/save via Fluent Support model API
246247
│ │ ├── class-fluent-support-module.php # FS: extends Helpdesk_Module_Base (uses Fluent_Support_Hooks trait)
247248
│ │ │
249+
│ │ ├── # ─── Marketplace (Dokan + WCFM + WC Vendors) ────
250+
│ │ ├── class-marketplace-module-base.php # Marketplace shared: vendor/sub_order/commission/payout patterns, settings, dedup, push/pull
251+
│ │ ├── class-marketplace-handler-base.php # Marketplace shared: status maps, sub-order load, PO line formatting
252+
│ │ ├── trait-dokan-hooks.php # Dokan: hook callbacks (vendor status, order status, withdraw)
253+
│ │ ├── class-dokan-handler.php # Dokan: vendor/sub-order/commission/payout data load
254+
│ │ ├── class-dokan-module.php # Dokan: extends Marketplace_Module_Base (uses Dokan_Hooks trait)
255+
│ │ ├── trait-wcfm-hooks.php # WCFM: hook callbacks (vendor status, order status, withdrawal)
256+
│ │ ├── class-wcfm-handler.php # WCFM: vendor/sub-order/commission/payout data load
257+
│ │ ├── class-wcfm-module.php # WCFM: extends Marketplace_Module_Base (uses WCFM_Hooks trait)
258+
│ │ ├── trait-wc-vendors-hooks.php # WC Vendors: hook callbacks (vendor status, order, payout)
259+
│ │ ├── class-wc-vendors-handler.php # WC Vendors: vendor/sub-order/commission/payout data load
260+
│ │ ├── class-wc-vendors-module.php # WC Vendors: extends Marketplace_Module_Base (uses WC_Vendors_Hooks trait)
261+
│ │ │
248262
│ │ ├── # ─── AffiliateWP ────────────────────────
249263
│ │ ├── class-affiliatewp-module.php # AffiliateWP: push-only, affiliates → res.partner, referrals → vendor bills
250264
│ │ ├── class-affiliatewp-handler.php # AffiliateWP: referral data load, vendor bill formatting, status mapping
@@ -410,14 +424,12 @@ WordPress For Odoo/
410424
│ │ ├── wp-db-stub.php # WP_DB_Stub ($wpdb mock with call recording)
411425
│ │ ├── plugin-stub.php # WP4Odoo_Plugin test singleton
412426
│ │ ├── wp-cli-utils.php # WP_CLI\Utils\format_items stub
427+
│ │ ├── constants-only.php # Consolidated constants for 14 third-party plugins (AMELIA_VERSION, WPCF_VERSION, etc.)
413428
│ │ ├── form-classes.php # GFAPI, GF_Field, wpforms() stubs
414429
│ │ ├── edd-classes.php # Easy_Digital_Downloads, EDD_Download, EDD_Customer, EDD\Orders\Order
415430
│ │ ├── memberpress-classes.php # MeprProduct, MeprTransaction, MeprSubscription
416431
│ │ ├── givewp-classes.php # Give class, give() function, GIVE_VERSION
417432
│ │ ├── charitable-classes.php # Charitable class with instance()
418-
│ │ ├── simplepay-classes.php # SIMPLE_PAY_VERSION constant
419-
│ │ ├── wprm-classes.php # WPRM_VERSION constant
420-
│ │ ├── amelia-classes.php # AMELIA_VERSION constant
421433
│ │ ├── bookly-classes.php # Bookly\Lib\Plugin class
422434
│ │ ├── learndash-classes.php # LEARNDASH_VERSION, LearnDash functions
423435
│ │ ├── lifterlms-classes.php # LLMS_VERSION, LLMS_Order, LLMS_Student
@@ -427,34 +439,25 @@ WordPress For Odoo/
427439
│ │ ├── events-calendar-classes.php # Tribe__Events__Main, Tribe__Tickets__Main
428440
│ │ ├── mec-classes.php # MEC class, MEC_VERSION constant
429441
│ │ ├── fooevents-classes.php # FooEvents class, FOOEVENTS_VERSION constant
430-
│ │ ├── fluent-booking-classes.php # FLUENT_BOOKING_VERSION constant
431442
│ │ ├── wc-bookings-classes.php # WC_Booking, WC_Product_Booking
432443
│ │ ├── sprout-invoices-classes.php # SI_Post_Type, SI_Invoice, SI_Payment
433444
│ │ ├── wp-invoice-classes.php # WPI_Invoice
434-
│ │ ├── crowdfunding-classes.php # WPCF_VERSION constant
435-
│ │ ├── ecwid-classes.php # ECWID_PLUGIN_DIR constant
436-
│ │ ├── shopwp-classes.php # SHOPWP_PLUGIN_DIR constant
437-
│ │ ├── job-manager-classes.php # WP_Job_Manager stubs
438445
│ │ ├── acf-classes.php # ACF functions (get_field, update_field, acf_get_field)
439446
│ │ ├── wc-points-rewards-classes.php # WC_Points_Rewards, WC_Points_Rewards_Manager
440447
│ │ ├── wc-bundles-classes.php # WC_Bundles, WC_Composite_Products, WC_Product_Bundle
441448
│ │ ├── awesome-support-classes.php # Awesome_Support, wpas_get_ticket_status
442449
│ │ ├── supportcandy-classes.php # WPSC_VERSION, SupportCandy stubs
443450
│ │ ├── affiliatewp-classes.php # AffiliateWP, affwp_get_affiliate
444-
│ │ ├── tutorlms-classes.php # TUTOR_VERSION constant
445451
│ │ ├── fluentcrm-classes.php # FLUENTCRM, FLUENTCRM_PLUGIN_VERSION, FluentCrm\App\Models stubs
446452
│ │ ├── funnelkit-classes.php # FunnelKit: WFFN_VERSION constant
447453
│ │ ├── gamipress-classes.php # GamiPress: GAMIPRESS_VERSION, gamipress(), points/achievement functions
448454
│ │ ├── buddyboss-classes.php # BuddyBoss: BP_VERSION, buddypress(), xprofile/groups functions
449-
│ │ ├── knowledge-classes.php # Knowledge module stubs (no WP plugin dependency, minimal)
450-
│ │ ├── wperp-classes.php # WP ERP stubs (WPERP_VERSION constant)
451-
│ │ ├── wperp-crm-classes.php # WP ERP CRM hooks/functions stubs
455+
│ │ ├── dokan-classes.php # Dokan stubs (dokan() function, classes)
456+
│ │ ├── wcfm-classes.php # WCFM stubs (WCFM() function, classes)
457+
│ │ ├── wc-vendors-classes.php # WC Vendors stubs (WCV_VERSION constant, classes)
452458
│ │ ├── wpai-classes.php # PMXI_VERSION, wp_all_import_get_import_id
453459
│ │ ├── jet-appointments-classes.php # JET_APB_VERSION, JET_APB\Plugin
454-
│ │ ├── jet-booking-classes.php # JET_ABAF_VERSION, JET_ABAF\Plugin
455-
│ │ ├── jetformbuilder-classes.php # JET_FORM_BUILDER_VERSION
456460
│ │ ├── jetengine-classes.php # JET_ENGINE_VERSION, Jet_Engine
457-
│ │ ├── wp-project-manager-classes.php # CPM_VERSION, WeDevs PM classes
458461
│ │ ├── wc-product-addons-classes.php # WC_PRODUCT_ADDONS_VERSION, WC_Product_Addons, THWEPO_VERSION, PPOM_VERSION
459462
│ │ ├── i18n-classes.php # WPML + Polylang stubs (constants, classes, functions)
460463
│ │ ├── wperp-accounting-classes.php # WP ERP Accounting stubs
@@ -468,10 +471,7 @@ WordPress For Odoo/
468471
│ │ ├── wc-shipping-classes.php # ShipStation, Sendcloud, Packlink, AST stubs
469472
│ │ ├── wc-returns-classes.php # YITH Returns, ReturnGO stubs
470473
│ │ ├── fluent-support-classes.php # FLUENT_SUPPORT_VERSION, FluentSupport model stubs
471-
│ │ ├── sensei-classes.php # SENSEI_LMS_VERSION, Sensei_Main stubs
472474
│ │ ├── ultimate-member-classes.php # UM class, UM_VERSION stubs
473-
│ │ ├── wc-rental-classes.php # WC Rental stubs (minimal)
474-
│ │ ├── field-service-classes.php # Field Service stubs (no WP plugin dependency)
475475
│ │ └── surecart-classes.php # SureCart stubs (SURECART_VERSION, SureCart model classes)
476476
│ ├── helpers/
477477
│ │ └── MockTransport.php # Shared mock Transport for API tests
@@ -584,6 +584,9 @@ WordPress For Odoo/
584584
│ ├── AffiliateWPModuleTest.php # Tests for AffiliateWP_Module
585585
│ ├── AffiliateWPHandlerTest.php # Tests for AffiliateWP_Handler
586586
│ ├── AffiliateWPHooksTest.php # Tests for AffiliateWP_Hooks
587+
│ ├── DokanModuleTest.php # Tests for Dokan_Module
588+
│ ├── WCFMModuleTest.php # Tests for WCFM_Module
589+
│ ├── WCVendorsModuleTest.php # Tests for WC_Vendors_Module
587590
│ ├── WPAllImportModuleTest.php # Tests for WP_All_Import_Module
588591
│ ├── OdooAccountingFormatterTest.php # Tests for Odoo_Accounting_Formatter
589592
│ ├── OdooModelTest.php # Tests for Odoo_Model enum
@@ -741,6 +744,13 @@ Module_Base (abstract)
741744
├── WC_Points_Rewards_Module → loyalty.card [bidirectional]
742745
├── Job_Manager_Module → hr.job [bidirectional]
743746
├── AffiliateWP_Module → res.partner (vendor), account.move (in_invoice) [WP → Odoo]
747+
├── Marketplace_Module_Base (abstract)
748+
│ ├── Dokan_Module → res.partner (vendor), purchase.order, account.move [bidirectional]
749+
│ │ (exclusive group: marketplace, requires: woocommerce)
750+
│ ├── WCFM_Module → res.partner (vendor), purchase.order, account.move [bidirectional]
751+
│ │ (exclusive group: marketplace, requires: woocommerce)
752+
│ └── WC_Vendors_Module → res.partner (vendor), purchase.order, account.move [bidirectional]
753+
│ (exclusive group: marketplace, requires: woocommerce)
744754
├── FluentCRM_Module → mailing.contact, mailing.list, res.partner.category [bidirectional]
745755
├── FunnelKit_Module → crm.lead, crm.stage [bidirectional]
746756
├── GamiPress_Module → loyalty.card, product.template [bidirectional]
@@ -772,6 +782,7 @@ Module_Base (abstract)
772782
- **Helpdesk**: Awesome Support, SupportCandy, and Fluent Support are mutually exclusive (all target `helpdesk.ticket` / `project.task`). First-registered wins — registration order: Awesome Support → SupportCandy → Fluent Support.
773783
- **Gamification**: GamiPress and myCRED are mutually exclusive (both target `loyalty.card` via `Loyalty_Card_Resolver` with `partner_id` + `program_id`). First-registered wins (GamiPress before myCRED in registration order).
774784
- **Events**: Events Calendar and MEC are mutually exclusive (both target `event.event` / `calendar.event` with exclusive group `events`). First-registered wins — registration order: Events Calendar → MEC. Note: FooEvents is independent (not in the exclusive group) — it coexists with either events module.
785+
- **Marketplace**: Dokan, WCFM, and WC Vendors are mutually exclusive (all target multi-vendor operations: `res.partner` vendors, `purchase.order` sub-orders, `account.move` commissions/payouts). First-registered wins — registration order: Dokan → WCFM → WC Vendors.
775786
- All other modules are independent and can coexist freely (LMS, Subscriptions, Points & Rewards, FooEvents, Booking, Donations, Forms, WPRM, Crowdfunding, BOM, WC Add-Ons, Jeero Configurator, AffiliateWP, FluentCRM, FunnelKit, BuddyBoss, Ultimate Member, Knowledge, Documents, WP ERP, WP ERP CRM, WP ERP Accounting, WP Project Manager, JetEngine, JetEngine Meta, ACF, WP All Import, Job Manager, Food Ordering, WC Rental, Field Service, Survey & Quiz).
776787

777788
**Module_Base provides** (uses traits: `Hook_Lifecycle`, `Translation_Accumulator`, `Sync_Orchestrator`, `Module_Helpers`):
@@ -811,15 +822,15 @@ Handler classes receive their dependencies via closures from their parent module
811822

812823
Both patterns are intentional and follow a clear convention:
813824

814-
- **Data Handlers** (pure transformers): `load_data()`, `parse_from_odoo()`, `save_data()`, `status_map()`. No Odoo API dependency. Examples: `Product_Handler`, `Order_Handler`, `WPRM_Handler`, `LearnDash_Handler`, `LifterLMS_Handler`, `TutorLMS_Handler`, `FluentCRM_Handler`, `Job_Manager_Handler`.
825+
- **Data Handlers** (pure transformers): `load_data()`, `parse_from_odoo()`, `save_data()`, `status_map()`. No Odoo API dependency. Examples: `Product_Handler`, `Order_Handler`, `WPRM_Handler`, `LearnDash_Handler`, `LifterLMS_Handler`, `TutorLMS_Handler`, `FluentCRM_Handler`, `Job_Manager_Handler`, `Dokan_Handler`, `WCFM_Handler`, `WC_Vendors_Handler`.
815826

816827
- **Integration Handlers** (with side effects): access plugin DB tables via `$wpdb`, call third-party APIs, or manage external state. Examples: `Amelia_Handler`, `Bookly_Handler`, `Ecwid_Handler`, `ShopWP_Handler`, `SimplePay_Handler`.
817828

818829
**Key rule**: a handler must NEVER call `Odoo_Client` directly — all Odoo interaction goes through the parent module. This preserves the module as the single synchronization orchestrator.
819830

820831
### Abstract Method Naming Convention
821832

822-
Intermediate base classes (`Membership_Module_Base`, `Dual_Accounting_Module_Base`, `Booking_Module_Base`, `Events_Module_Base`, `Helpdesk_Module_Base`, `LMS_Module_Base`) define abstract methods for subclass configuration. Two naming prefixes distinguish their purpose:
833+
Intermediate base classes (`Membership_Module_Base`, `Dual_Accounting_Module_Base`, `Booking_Module_Base`, `Events_Module_Base`, `Helpdesk_Module_Base`, `LMS_Module_Base`, `Marketplace_Module_Base`) define abstract methods for subclass configuration. Two naming prefixes distinguish their purpose:
823834

824835
- **`handler_*()`** — delegates to the handler class for data operations (load, save, parse, delete). These methods interact with the WordPress database or plugin APIs. Examples: `handler_load_level()`, `handler_load_child()`, `handler_parse_service_from_odoo()`, `handler_save_service()`.
825836

@@ -2338,6 +2349,55 @@ All user inputs are sanitized with:
23382349

23392350
**Settings:** `sync_courses`, `pull_courses`, `sync_orders`, `sync_enrollments`
23402351

2352+
### Dokan — COMPLETE
2353+
2354+
**Files:** `class-dokan-module.php` (extends `Marketplace_Module_Base`, uses `Dokan_Hooks` trait), `trait-dokan-hooks.php` (hook callbacks), `class-dokan-handler.php` (extends `Marketplace_Handler_Base`, vendor/sub-order/commission/payout data)
2355+
2356+
**Odoo models:** `res.partner` (vendors as suppliers), `purchase.order` (sub-orders), `account.move` (commissions as vendor bills, payouts as payments)
2357+
2358+
**Key features:**
2359+
- Multi-vendor marketplace sync via `Marketplace_Module_Base`
2360+
- Exclusive group: `marketplace` (conflicts with WCFM, WC Vendors)
2361+
- Requires WooCommerce (`required_modules: ['woocommerce']`)
2362+
- Vendors → res.partner (suppliers), status bidirectional
2363+
- Sub-orders → purchase.order with line items
2364+
- Commissions → vendor bills (`in_invoice`)
2365+
- Withdrawals → payments (auto-post bills)
2366+
- Partner chain: vendor user → Odoo partner (pre-sync on push)
2367+
- Detection: `dokan()` function
2368+
2369+
**Settings:** `sync_vendors`, `sync_sub_orders`, `sync_commissions`, `sync_payouts`, `pull_vendors`, `auto_post_bills`
2370+
2371+
### WCFM — COMPLETE
2372+
2373+
**Files:** `class-wcfm-module.php` (extends `Marketplace_Module_Base`, uses `WCFM_Hooks` trait), `trait-wcfm-hooks.php` (hook callbacks), `class-wcfm-handler.php` (extends `Marketplace_Handler_Base`, vendor/sub-order/commission/withdrawal data)
2374+
2375+
**Odoo models:** `res.partner` (vendors as suppliers), `purchase.order` (sub-orders), `account.move` (commissions as vendor bills, withdrawals as payments)
2376+
2377+
**Key features:**
2378+
- Multi-vendor marketplace sync via `Marketplace_Module_Base`
2379+
- Exclusive group: `marketplace` (conflicts with Dokan, WC Vendors)
2380+
- Requires WooCommerce (`required_modules: ['woocommerce']`)
2381+
- Detection: `WCFM()` function
2382+
- Same entity pattern as Dokan (shared via base class)
2383+
2384+
**Settings:** `sync_vendors`, `sync_sub_orders`, `sync_commissions`, `sync_payouts`, `pull_vendors`, `auto_post_bills`
2385+
2386+
### WC Vendors — COMPLETE
2387+
2388+
**Files:** `class-wc-vendors-module.php` (extends `Marketplace_Module_Base`, uses `WC_Vendors_Hooks` trait), `trait-wc-vendors-hooks.php` (hook callbacks), `class-wc-vendors-handler.php` (extends `Marketplace_Handler_Base`, vendor/sub-order/commission/payout data)
2389+
2390+
**Odoo models:** `res.partner` (vendors as suppliers), `purchase.order` (sub-orders), `account.move` (commissions as vendor bills, payouts as payments)
2391+
2392+
**Key features:**
2393+
- Multi-vendor marketplace sync via `Marketplace_Module_Base`
2394+
- Exclusive group: `marketplace` (conflicts with Dokan, WCFM)
2395+
- Requires WooCommerce (`required_modules: ['woocommerce']`)
2396+
- Detection: `WCV_VERSION` constant
2397+
- Same entity pattern as Dokan/WCFM (shared via base class)
2398+
2399+
**Settings:** `sync_vendors`, `sync_sub_orders`, `sync_commissions`, `sync_payouts`, `pull_vendors`, `auto_post_bills`
2400+
23412401
### Ultimate Member — COMPLETE
23422402

23432403
**Files:** `class-ultimate-member-module.php` (extends `Module_Base`), `class-ultimate-member-handler.php` (profile/role load/save via UM API), `trait-ultimate-member-hooks.php` (registration, update, delete, role change hooks)

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010
### Fixed
1111
- **WP Crowdfunding compatibility** — Updated module detection from `wpneo_crowdfunding_init()` function to `WPCF_VERSION` constant, meta keys from `_wpneo_*` to `_nf_*` prefix, and version constant from `STARTER_VERSION` to `WPCF_VERSION`, matching WP Crowdfunding 2.x API surface
1212

13+
### Changed
14+
- **Marketplace base classes** — Extracted shared logic from Dokan, WCFM, and WC Vendors into `Marketplace_Module_Base` and `Marketplace_Handler_Base`, eliminating ~477 lines of duplicated code across 6 module/handler files
15+
- **LMS handler template method** — Added `load_course()` template method to `LMS_Handler_Base` with 3 abstract hooks (`get_course_post_type()`, `get_course_price()`, `get_lms_label()`), eliminating identical implementations from 5 LMS handlers (~80 lines)
16+
- **LMS module shared enrollment** — Moved identical `load_enrollment_data()` from 5 LMS modules into `LMS_Module_Base` via `get_lms_handler()` abstract (~35 lines)
17+
- **Test stub consolidation** — Consolidated 14 single-constant stub files into `constants-only.php` and removed 4 empty stubs, reducing `bootstrap.php` requires from 68 to 50
18+
- **PHPDoc generics** — Added `array<string, mixed>` return types to 11 methods in `Module_Base` and `Settings_Repository`
19+
1320
## [3.9.0] - 2026-02-19
1421

1522
### Fixed (Architecture)

0 commit comments

Comments
 (0)