FCHubFCHub.co

Changelog

Every version of the plugin that traded your WooCommerce data for a FluentCart future.

What shipped, when it shipped, and what changed between one version and the next.


March 2026

v1.1.0

Released 15 March 2026

A complete rewrite. The old codebase was a flat script with no separation of concerns — a god-class controller, static mappers, and enough corrupted JSON to make a database cry. We burned it down and rebuilt it properly. PER-CS 3.0, PHP 8.3, modular architecture, and 182 tests that actually prove things work.

If you ran a migration with v1.0.x, bless your heart. This version fixes every data integrity issue we found — and we found a lot.

Architecture

Rebuilt from scratch following the fchub-memberships module pattern. DI container, feature flags, interface contracts, the works. The old 382-line AdminController is now four focused controllers that delegate to domain services. Mappers are instance classes with constructor injection instead of bags of static methods. Every class is final unless it's designed for inheritance.

The admin UI moved from 770 lines of vanilla JS string concatenation to Vue 3 with Vite. Reactive state, composables, scoped styles, and a build step that outputs 99KB. The kind of frontend you can actually debug without questioning your life choices.

Data integrity fixes (the scary ones)

  • Double JSON encoding — nine fields across six mappers called json_encode() before passing arrays to FluentCart models that already encode internally. Every piece of metadata in every migrated record was corrupted. Fixed everywhere.
  • Order status mappingpending mapped to a status FluentCart doesn't recognise. cancelled mapped to failed. Both wrong. Now maps correctly using backed PHP 8.3 enums.
  • Order type — was onetime (not a thing in FC). Now checkout, matching FC's own migrator.
  • Guest customer IDs — used crc32() which collides at ~77k emails and produces negative integers on 64-bit PHP. Now uses the email string directly as the key.
  • Null foreign keys — if a customer wasn't migrated, the order was created with customer_id = 0. Now validates all FKs before creating records and skips with a logged warning.
  • Rollback deleted pre-existing data — if a customer already existed in FC, rollback would delete them. Now tracks created_by_migration and only rolls back what the migration actually created.
  • Zero-decimal currencies — JPY, KRW, and 14 others were multiplied by 100, turning ¥1000 into ¥100,000. Now handled correctly.
  • Tax behaviour inverted — inclusive tax was stored as exclusive and vice versa. The trifecta of wrong.
  • Unit price from discounted total — items with coupons had incorrect per-unit pricing. Now derives from the pre-discount subtotal.

New data migrated

Products get gallery images, variation thumbnails, download files, weight/dimensions, default variation selection, shipping classes, product visibility (private preserved, hidden mapped), brands, tags (as searchable meta), and FC attribute system integration.

Orders get applied coupons, fee line items as proper FC records, per-item refund tracking, shipping line details, order notes, key meta fields (transaction ID, customer note, phone numbers), invoice numbers (WC-{id}), exchange rates, and partial refund status sync.

Subscriptions get Stripe/PayPal vendor IDs, multi-item warnings, and quarterly/half-yearly billing intervals.

Customers get purchase stats recalculated post-migration: LTV, AOV, purchase count, first/last purchase dates — all matching FC's actual schema including the JSON-by-currency purchase_value format.

Coupons get FC's real condition schema with product/category restrictions and email restrictions.

Migration engine

  • Batch processing — each batch processes 50 records then returns immediately. The UI drives batches via REST for real-time progress. No more single-request timeout bombs.
  • Action Scheduler — available for WP-CLI background processing. The web UI uses direct REST batches for instant feedback.
  • Dry run — actually works now (it was a complete no-op before — the flag travelled through the stack and did nothing). Validates every record without writing to the database. Log entries show exactly what would happen.
  • Transaction wrapping — each record wrapped in START TRANSACTION / COMMIT / ROLLBACK. Partial data on failure is impossible.
  • Cache managementwp_cache_flush() every 5 batches to prevent memory exhaustion on large stores.
  • HPOS compatible — all order queries use the wc_orders table, not wp_posts.

Preflight

Eight checks now, up from five: WooCommerce, FluentCart, WC Subscriptions (optional), PHP memory, max execution time, product type breakdown (warns about unsupported types), existing FC data, and migration table existence.

WP-CLI

Five commands: wp cartshift migrate, rollback, status, log, finalize. The migrate command runs synchronously with progress bars per entity. log supports --format=table|json|csv and --status filtering.

Admin UI

Vue 3 + Vite. Preflight checks with status badges, entity selection with auto-dependency resolution, real-time progress bars, expandable migration log with search/filter/load-more, CSV export, dark mode sync with FluentCart's theme.

Rollback improvements

Uses wp_delete_post() for products (not raw SQL — the old way left orphaned meta, terms, and thumbnails). Includes guest customers. Respects the created_by_migration flag. Follows Constants::ROLLBACK_ORDER for dependency-safe deletion.

Post-migration finalization

New POST /cartshift/v1/finalize endpoint recalculates customer purchase stats (LTV, AOV, purchase count, dates) and clears caches. Available via the admin UI button and wp cartshift finalize.

v1.0.3

Released 6 March 2026GitHub Release

Fixed a fatal error when multiple FCHub plugins are active at the same time. The shared GitHubUpdater class used a class_exists guard that PHP's OPcache cheerfully ignored during early class binding — so the second plugin to load would redeclare the class and take the site down. Wrapped the class inside the conditional so OPcache actually respects it.

v1.0.2

Released 3 March 2026GitHub Release

Raised the floor. PHP 8.1 and WordPress 6.4 are now the documented minimums. No functional changes — if it worked before, it still works. If you were running PHP 7.4, now you have a nudge.

Requirements bump

  • Minimum PHP raised from 7.4 to 8.1
  • Minimum WordPress raised from 6.0 to 6.4

v1.0.1

Released 3 March 2026GitHub Release

Version bump only. The first release under the monorepo slash-tag convention (cartshift/v*). No code changed. v1.0.0 shipped under the old wc-fc-v1.0.0 tag format; this corrects that.

v1.0.0

Released 2 March 2026GitHub Release

The initial release. A complete one-time migration tool for moving WooCommerce data into FluentCart — products, customers, orders, subscriptions, and coupons. The kind of thing you only run once, but had better run correctly.

One-time tool

CartShift is designed for a single migration run. Once you have verified your data in FluentCart, deactivate and delete the plugin. It has no ongoing function after migration is complete.

Migration suite

Five entity types, migrated in dependency order regardless of what you ask for:

EntityWhat moves across
ProductsSimple and variable products, categories, variations, featured images, SKUs, stock settings, fulfillment type
CustomersRegistered customers and guest emails extracted from orders, with billing and shipping addresses
CouponsDiscount codes, types (percentage/fixed), amounts, expiry dates, usage limits, conditions
OrdersOrder records, line items, addresses, payment transactions, refund transactions, status mapping
SubscriptionsBilling intervals, trial periods, signup fees, renewal dates, status mapping — requires WC Subscriptions

Grouped and external products are skipped. Subscription migration is silently skipped if WC Subscriptions is not active.

Preflight checks

Run before touching any data. Five checks:

  • WooCommerce active — hard requirement, blocks migration if missing
  • FluentCart active — hard requirement, blocks migration if missing
  • WC Subscriptions — informational, determines whether subscription migration is available
  • PHP memory — warns below 256 MB, does not block
  • Existing FluentCart data — warns if FluentCart already contains records, does not block

The ready flag in the preflight response is true only when both required checks pass.

Dry run mode

Pass "dry_run": true to the migrate endpoint and every record is validated and logged without writing anything to FluentCart. Use it to find errors before committing. The migration log stays intact after dry runs so you can review what would have happened.

Batch processing

All entity types process in batches of 50 records. Peak memory usage stays predictable regardless of store size. A store with 10,000 orders runs in 200 batches.

ID mapping

Every successfully migrated record is tracked in wp_cartshift_id_map — a permanent mapping between WooCommerce IDs and FluentCart IDs. Orders use this to look up the correct customer and product IDs. Rollback uses it to find and delete exactly the right records.

Migration log

Every processed record (success, skip, or error) writes to wp_cartshift_migration_log. Queryable via REST API with pagination. Survives rollback so you can audit what happened.

Rollback

Changed your mind? The rollback endpoint deletes all FluentCart records created by the migration — in reverse dependency order — using the ID map. Pre-existing FluentCart data is untouched. The migration log is preserved. After rollback, the ID map is cleared and you can start again from scratch.

Status and type mapping

Both order statuses and subscription statuses are translated to their FluentCart equivalents. Order types are inferred from their contents: regular orders become onetime, orders with subscription products become subscription, WC renewal orders become renewal.

HPOS support

Customer counting and guest email extraction work with both HPOS (High-Performance Order Storage) and legacy post-based order storage.

Deduplication

  • Customers with the same email already in FluentCart are linked rather than duplicated
  • Coupons with the same code are skipped with the mapping stored
  • Duplicate product SKUs get a -wc{id} suffix to stay unique
  • Product categories matched by slug are reused, not duplicated

On this page