=== Factorize Catalog ===
Contributors: webyoungstown
Tags: woocommerce, sorting, search, featured, merchandising
Requires at least: 6.2
Tested up to: 6.7
Stable tag: 1.0.1
Requires PHP: 7.4
License: Factorize License (proprietary)
License URI: https://factorize.webyoungstown.com/license/

Best-deals-first sorting for WooCommerce: stock-first ordering, SKU search, sold-this-week badges, auto Featured picks, and infinite scroll.

== Description ==

Default WooCommerce sorting was not built for a liquidation, bin, or open-box store. Out-of-stock items clutter every page, the "popularity" sort only knows what *your* store has sold, discounts play no part in ordering at all, and a customer who types a model number ("2625-20") or a UPC into search finds nothing, because WordPress only searches titles and content. Factorize Catalog fixes all of that: it sorts every listing the way a merchandiser would — in stock first, proven sellers up, deepest discount wins — and makes search actually find products by SKU.

The whole default ordering runs off one precomputed integer per product, so it stays a single indexed query. It was built and tuned on a real 950-product liquidation store.

= Features =

* **"Best deals" catalog sort, set as the store default.** A precomputed rank meta (`_et_rank`) encodes four signals into one integer — in-stock status, units sold this week, lifetime units sold, and percent off — so the sort is a plain indexed `meta_value_num` query instead of a slow multi-key meta join.
* **Banded ranking that a lower signal can never outweigh.** In stock always outranks out of stock; this week's sales (capped at 9,999) outrank lifetime sales (capped at 999); percent off (capped at 99) breaks ties. What is selling *now* deliberately beats what sold well a year ago.
* **Automatic rank maintenance.** Ranks recompute on product save and create, on every stock change, and on order status changes (which also refresh the weekly-sales figures). No cron to configure.
* **One-time backfill helper.** After an import, `wp eval "empire_tools_rebuild_ranks();"` recomputes the rank for every product in the catalog.
* **"Most popular" catalog sort** driven by an industry popularity score — what sells most in the market at large, not just in your store. WooCommerce's own store-sales popularity option is removed to avoid a confusing duplicate (your store's real sales already power "Best deals").
* **Industry popularity scoring.** A curated demand table (product-type keywords crossed with brand market-share multipliers) produces a 0-100 `et_popularity` score from each product's title, computed automatically when a product is added or retitled. It is never rendered on the storefront — it powers the Featured picker and the "Most popular" sort.
* **Out-of-stock sinks to the bottom of every catalog sort** — price, newest, rating, all of them — via one indexed join against WooCommerce's `wc_product_meta_lookup` table. Items are demoted, never hidden.
* **SKU and model-number search.** Site search also matches the `_sku` meta, spliced inside WordPress's own search group so post type and status filters stay intact.
* **Exact-SKU search goes straight to the product.** A search for a full SKU or product number redirects to that product's page — even when it is out of stock, because someone typing the exact number wants that exact item.
* **Search results ordered sensibly.** An exact SKU hit ranks first, then in-stock items, then normal WordPress relevance.
* **"X sold this week" social proof badges** on shop cards and product pages, counted from the last 7 days of processing and completed orders. A quiet product shows nothing rather than "0 sold". Counts are cached for an hour and refreshed the moment an order changes status.
* **Automatic Featured picker.** `empire_tools_refresh_featured()` fills WooCommerce's Featured flag with the top industry-popularity products that are in stock and photographed, falling back to the best-ranked deals when too few products are scored yet. The slot count is configurable (default 4), and it only writes when the selection actually changes.
* **Infinite scroll on shop, category, and search archives.** Real numbered pagination stays underneath as the source of truth (SEO, page URLs, and no-JS visitors keep working) while shoppers get continuous loading with a spinner and a clean "that's everything" end note. Add-to-cart keeps working on appended cards.
* **Factorize admin hub card** under the Selling tab, showing the live Featured slot count.
* **Clean uninstall.** Deleting the plugin removes only its own options. Product meta and store history are deliberately kept — deleting a plugin should never delete store data.
* **Translation-ready**, with all strings in the `factorize-catalog` text domain.

= Works standalone, better together =

Factorize is a modular WooCommerce toolkit for liquidation, bin, discount, and open-box stores, built and battle-tested on a real 950-product store. Every plugin works completely standalone; any of them alone stands up the shared "Factorize" admin hub, and together they fill it with category tabs (Insights / Selling / Customers / Storefront).

Two honest notes for this plugin specifically:

* The percent-off signal in the deal rank calls `empire_tools_discount_pct()`, which ships with Factorize Essentials. Keep Essentials active alongside (recommended), or provide that function yourself — search, stock-first ordering, popularity scoring, badges, and infinite scroll have no such dependency.
* Nothing in this plugin schedules the Featured refresh on its own. Factorize Auctions' hourly tick calls it automatically when installed; standalone, run `wp eval "empire_tools_refresh_featured();"` on demand or from your own cron.

= Configuration =

All configuration is read through `empire_catalog_config( $key )`: defaults are merged with the `factorize-catalog_options` option (an array — override any subset of keys; the legacy `empire-catalog-ranking_options` option is still honored), and the merged result passes through the `factorize-catalog_config` filter, so either mechanism can override any key.

* `featured_limit` (default `4`) — how many products the Featured picker selects.

Example: `update_option( 'factorize-catalog_options', array( 'featured_limit' => 6 ) );`

= Roadmap =

These features are planned and do **not** exist in this release yet:

* Manual pin/boost per product
* Exclude-from-Featured flag
* A/B sort experiments
* Analytics-blended ML re-ranking
* Scheduled Featured rotations

== Installation ==

1. Upload the `factorize-catalog` folder to `/wp-content/plugins/`, or install the zip via Plugins > Add New > Upload Plugin, then activate it. WooCommerce must be active — the plugin is inert without it.
2. Recommended: also activate Factorize Essentials, which provides the `empire_tools_discount_pct()` function the deal rank uses for its percent-off signal.
3. That is it for the storefront: "Best deals" and "Most popular" appear in the shop's sorting dropdown with "Best deals" as the default, out-of-stock items sink in every sort, and SKU search and infinite scroll are live. The plugin's status card appears under the new "Factorize" menu in wp-admin, on the Selling tab.
4. First thing to do on an existing catalog: backfill the ranks once with WP-CLI — `wp eval "empire_tools_rebuild_ranks();"`. From then on, ranks maintain themselves on saves, stock changes, and orders.
5. Optional: populate Featured products with `wp eval "empire_tools_refresh_featured();"` (Factorize Auctions runs this hourly when installed), and adjust the slot count via the `factorize-catalog_options` option.

== Frequently Asked Questions ==

= Does it work without the other Factorize plugins? =

Almost entirely, yes. The sorts, SKU search, stock-first ordering, badges, popularity scoring, infinite scroll, and the Factorize admin hub all work with this plugin alone. Two caveats: the deal rank's percent-off signal calls `empire_tools_discount_pct()` from Factorize Essentials (activate it, or define that function yourself), and the Featured refresh is only scheduled automatically by Factorize Auctions — standalone, trigger it via WP-CLI or your own cron.

= Does it require WooCommerce? =

Yes, for anything useful. Every WooCommerce call is hooked to a WooCommerce action or guarded with `function_exists()`, so the plugin is safely inert if WooCommerce is deactivated.

= Where does the data live? Does anything leave my server? =

Everything is local. The plugin stores two post meta values per product (`_et_rank` and `et_popularity`) and one hourly transient of weekly sales counts. The industry popularity table is bundled in the plugin code. There are no external API calls, no tracking, and nothing ever leaves your server.

= Why doesn't the shop order change right after activation? =

On an existing catalog, `_et_rank` is empty until each product is saved. Run the one-time backfill — `wp eval "empire_tools_rebuild_ranks();"` — and the default sort takes effect immediately.

= Why does Featured look like a plain best-discount list on a new store? =

Two fallbacks stacking: with too few `et_popularity` scores, the Featured picker fills from the deal rank, and with no orders yet both sales terms of the rank are zero — so percent off decides. As popularity scores seed (on product save) and real orders arrive, Featured and the sorts sharpen on their own.

= How do I change how many Featured products are picked? =

Set `featured_limit` via the option — `update_option( 'factorize-catalog_options', array( 'featured_limit' => 6 ) );` — or return it from the `factorize-catalog_config` filter. The next refresh uses the new limit.

== Screenshots ==

1. The shop page under the default "Best deals" sort — in-stock, fast-selling, deeply discounted products first, with "sold this week" badges on the cards.
2. The sorting dropdown with "Best deals" and "Most popular" added and WooCommerce's store-sales popularity option removed.
3. A search for a model number: partial matches list in-stock items first; an exact SKU match redirects straight to the product page.
4. Infinite scroll on a category archive — spinner while the next page loads, and the "that's everything" note at the end of the catalog.
5. The Factorize admin hub with the Factorize Catalog card on the Selling tab, showing the Featured slot count.

== Changelog ==

= 1.0.0 =
* Initial release.
* "Best deals" default sort from a single precomputed rank (in stock, weekly sales, lifetime sales, percent off).
* "Most popular" sort and 0-100 industry popularity scoring from product titles.
* SKU search with exact-match redirect; out-of-stock demoted in every catalog sort and in search.
* "X sold this week" badges on shop cards and product pages.
* Automatic Featured picker with configurable slot count and best-deal fallback.
* Infinite scroll on shop, category, and search archives with SEO-safe pagination underneath.
