Skip to content

feat(cli): add fix-memberships command (NPPM-386)#4733

Draft
adekbadek wants to merge 1 commit into
trunkfrom
feat/fix-memberships-cli
Draft

feat(cli): add fix-memberships command (NPPM-386)#4733
adekbadek wants to merge 1 commit into
trunkfrom
feat/fix-memberships-cli

Conversation

@adekbadek
Copy link
Copy Markdown
Member

All Submissions:

Changes proposed in this Pull Request:

Transplants wp newspack-subscription-migrations fix-memberships from newspack-subscription-migrations#36 into newspack-plugin as wp newspack fix-memberships.

Addresses NPPM-386.

Why move it here:

  • It lives next to the closely-related wp newspack migrate-expired-subscriptions and wp newspack teams-for-memberships diagnostics commands. Same shape (single-site WC Memberships/Subscriptions cleanup CLI), same gating pattern (function_exists( 'wc_memberships' )).
  • Ships with every Newspack install instead of requiring publishers to side-load newspack-subscription-migrations.
  • Closes Muriel-styled Text/Number Input component. #36 as part of this PR (see comment there).

What the command does (dry-run by default, --live to apply):

  1. Primary pass – walks users who have an active subscription for a plan product but no active membership, and:
    • Reclaims a network-managed membership when a local subscription exists (strips _managed_by_newspack_network / _remote_id / _remote_site_url and relinks to the local subscription).
    • Relinks subscription-linked memberships that lost their _product_id meta.
    • Reactivates inactive memberships whose subscription is active (clears _end_date and unschedules expiration events to avoid re-expiry via cron).
    • Creates a new membership when none exists for the plan/user.
  2. Second pass – clears stale _end_date on memberships whose linked subscription is still active (independent of the primary pass).

Handles:

  • Team products (_wc_memberships_for_teams_plan meta on products) and team plans.
  • Variable / variable-subscription products (expands to children).
  • Transferred subscriptions (billing email belongs to a different WP user who already has a membership linked to the subscription – skipped to avoid duplicates).
  • Team subscription payers (skipped when they have no membership for the plan).
  • HPOS-enabled sites (branches the subscription join on woocommerce_custom_orders_table_enabled).
  • Network-managed memberships (per-membership classification rather than a blanket skip).

Differences vs the source #36:

  • Conformed to newspack-plugin namespacing (Newspack\CLI\Fix_Memberships) and the existing CLI registration pattern in class-initializer.php (gated on wc_memberships, like the teams-diagnostics command is gated on WC_Memberships_For_Teams_Loader).
  • Split the original 700-line fix_memberships method into focused private helpers (process_user, handle_user_without_local_membership, handle_user_with_inactive_membership, etc.).
  • Extracted the repeated 4-call reactivation sequence into a reactivate_if_inactive( $membership ) helper used everywhere a reactivation was open-coded in Muriel-styled Text/Number Input component. #36 (5 sites).
  • Extracted clear_network_managed_meta( $membership_id ) for the three _managed_by_newspack_network / _remote_id / _remote_site_url deletions.
  • Method renamed from fix_memberships to run to avoid Generic.NamingConventions.ConstructorName.OldStyle (the class is Fix_Memberships, so a fix_memberships method tripped the PHP4 constructor detector).
  • Removed the always-true default for --verbose. Opt-in like every other Newspack CLI.

Worth considering as a follow-up (not done here): Membership_Expiry::prevent_membership_expiration() does a structurally similar reactivation when an alternate active subscription is found. It currently omits unschedule_expiration_events() + set_end_date( '' ) because it runs as a pre-expiration filter; this PR's helper does both. Pulling the helper out into something Membership_Expiry can also call would let both code paths share one implementation. Left out of this PR to keep behavior of existing code unchanged.

How to test the changes in this Pull Request:

  1. Local site with WooCommerce, WooCommerce Subscriptions, and WooCommerce Memberships active.
  2. Create a membership plan with at least one linked product, plus a customer with an active subscription for that product.
  3. Manually break the membership state (e.g. set the membership to expired while the subscription is still active, or delete the membership entirely).
  4. Run wp newspack fix-memberships --verbose and confirm the dry-run output describes the right corrective action without changing data.
  5. Run wp newspack fix-memberships --verbose --live and confirm the membership is reactivated / relinked / created.
  6. Optional: with newspack-network installed, mark a membership as _managed_by_newspack_network while a local subscription exists, and verify the command reclaims it under --live.
  7. Optional: enable HPOS (woocommerce_custom_orders_table_enabled=yes) and re-run – the command should detect HPOS and use the orders-table join.

Other information:

  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Have you written new tests for your changes, as applicable? – no automated tests; behavior is exercised on data the local env doesn't have. See the manual test plan above.
  • Have you successfully ran tests with your changes locally? – PHPCS clean; PHP syntax clean.

Reconciles wc_user_membership posts against the user's active subscriptions:
relinks orphaned memberships, reactivates expired/cancelled memberships whose
subscription is still active, creates missing memberships, and reclaims
network-managed memberships when a local subscription exists.

Transplanted from newspack-subscription-migrations#36.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant