=== Driftgauge Sync Auditor ===
Contributors: dirkdigger
Tags: woocommerce, inventory, stock, sync, csv
Requires at least: 6.0
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 0.1.0
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Compare your WooCommerce inventory against any supplier feed, CSV import or marketplace export, and see every difference before you trust it.

== Description ==

**A dry run with a receipt.** Point it at a supplier feed, a marketplace export,
or the CSV you were about to import, and get a line-by-line account of the
difference between that file and your WooCommerce catalogue: which SKUs are
missing on each side, where the quantity disagrees, where the price disagrees,
and what the file does not let us decide. Nothing is written to your store.

It exists because the usual way to discover a failed inventory sync or a bad CSV
import is by accident — a customer asks why something shows out of stock, a
product quietly disappeared, or a charge turns up on a card statement weeks
later. After a sync error or an import error it tells you what actually changed;
before the next one runs, it tells you what would. It is built for checking a
supplier feed, a dropship price list or an ERP export before you trust it with
your live stock levels.

= What it finds =

* **Missing SKUs** — rows that exist in the file but not in your store, and
  products in your store that the file never mentions.
* **Quantity mismatch** — stock levels that disagree between the two sources.
* **Price mismatch** — prices that disagree, compared as regular price, sale
  price, or "effective" price (sale price where present, otherwise regular).
* **Silent reversion** — a value that is back to something it held in an earlier
  run of the same audit, which is what a half-applied or rolled-back sync
  usually looks like afterwards.
* **Rows that cannot be decided** — reported as undecidable, never guessed.

= When to use it =

* Before a product import from a supplier CSV, to see what that import would
  change.
* After a stock sync that looks wrong, to see what actually differs right now.
* When a product feed or marketplace export starts overwriting your prices.
* When stock levels drift, to get the exact list of SKUs that disagree instead
  of just knowing something is off.
* As a recurring stock reconciliation: run the same inventory audit every month
  as part of your inventory management routine, and let the revert detection tell
  you whether anything moved back.

= What it refuses to do =

These are deliberate, and they are the point:

1. **It never silently skips a row it cannot parse.** A value that is present but
   unreadable — `abc` where a number belongs — is listed under FAILURES and the
   run is marked as untrustworthy.
2. **It never picks a winner when a SKU is duplicated.** Choosing one would hide
   exactly the corruption you went looking for.
3. **It never claims a row is "missing" when the other side failed to load
   completely.** Those rows are reported as *cannot be asserted*.
4. **It never invents a number.** An empty quantity or price means unknown: the
   row is still matched by SKU, it is not compared on that field, and the count
   is stated under Notes. Absent data and damaged data are different things, and
   treating the first as the second would quietly disable the findings that
   matter most.
5. **It assigns no score, no rating, no red/green light, and gives no advice.**
   It states differences; you decide what they mean.

= Why not just read the sync log? =

Because the sync log tells you what the sync *tried* to do. This tells you what
is true right now on both sides, computed from the data itself.

== Installation ==

1. Upload the plugin folder to `/wp-content/plugins/`, or install the ZIP
   through Plugins → Add New → Upload Plugin.
2. Activate it through the Plugins menu.
3. Go to **Tools → Driftgauge Audit**, upload a CSV (or leave the left side
   empty to use your WooCommerce catalogue), and run the audit.

No API keys, no external service. Uploaded files are parsed in memory from the
temporary upload path and are never stored by the plugin.

== Frequently Asked Questions ==

= How do I check whether a CSV import or an inventory sync went wrong? =
Run the audit with the file you imported (or a fresh export from the other side)
as one side, and your catalogue as the other. Every SKU that disagrees on
quantity or price, every row present on only one side, and every value that
reverted since the previous run is listed explicitly. Running it changes nothing.

= Can I use it as a regular stock reconciliation report? =
Yes. Run the same comparison on a schedule and keep the reports: each run is
compared against the previous one, so a value that silently moved back is called
out as a reversion instead of looking like a fresh difference.

= Does it modify my products? =
No. It reads, compares and reports. There is no write path to products, orders
or any other store data. The only thing it saves is its own small snapshot
option, which is what lets it tell you that a value reverted.

= What if my file has different column names? =
Common headers are recognised automatically in English and Chinese — for
example `SKU`, `Item Code`, `Variant SKU`, `货号`, `Qty`, `On Hand`, `库存`,
`Price`, `Regular price`, `单价`. For anything else the audit stops rather than
guessing, and the failure message names the field it could not find.

= Does it work with Shopify or marketplace exports? =
Yes. Shopify's variant export and WooCommerce's own product export are recognised
as formats, including the parent/variation rows those exports write without a
SKU — those are structure, not errors. Any other CSV is read by its headings.

= What does "cannot be asserted" mean? =
If one side failed to load some rows, a row that appears only on the other side
is not proof of anything. The plugin says so instead of reporting a false
difference.

= How large a file can it handle? =
The comparison is linear. A 20,000-row pair is processed in well under a second
on a normal machine; the practical limit is your upload and memory limits.

= What about products that do not track stock? =
They are read and matched by SKU, but their quantity is unknown — there is no
number to compare — so the row is not compared on quantity and the count of such
rows is listed under Notes. It is deliberately not treated as zero: that would
invent a quantity and then report a confident, false "quantity disagrees" against
every matching row in the other file. It is also not treated as damaged data,
because that would make the whole side untrustworthy and suppress the
"missing on the other side" findings — the most useful thing this tool reports.

== Screenshots ==

1. The audit result: summary counts, then one table per kind of difference.
2. The FAILURES panel, listing rows that could not be compared and why.

== Changelog ==

= 0.1.0 =
* First release: SKU-matched quantity and price comparison, WooCommerce and
  Shopify export formats, revert detection, explicit failure reporting.
* Read-only by design.

== Upgrade Notice ==

= 0.1.0 =
Initial release.

== Development notes ==

The comparison engine is deliberately free of WordPress dependencies so it can
be tested on the command line:

    php tests/run-tests.php            # engine, 33 checks
    php tests/run-wp-admin-tests.php   # admin layer against function stubs, 23 checks

And inside a real install (from the WordPress root):

    php wp-content/plugins/driftgauge-sync-auditor/tests/run-real-wp-tests.php
    php wp-content/plugins/driftgauge-sync-auditor/tests/run-real-wp-woo-tests.php

The real-WordPress suite covers plugin loading, the Tools menu (registered for an
administrator, absent for a subscriber), the capability gate, a valid nonce being
accepted and a forged one rejected, the snapshot option, revert detection across
runs, and a read-only proof that no posts, post meta or other options are
written. Run on **WordPress 7.1.3 / PHP 8.4.26: 20/20**.

The WooCommerce suite covers the catalogue path: it creates products, then audits
the catalogue against a CSV and checks that the matching product is not reported
as drift, that quantity drift is found, that a CSV-only SKU is reported as
missing, that variations are read, that a product with no SKU is never silently
matched, and that a catalogue containing untracked-stock or container rows still
reports the genuinely missing SKU. Run on
**WordPress 7.1.3 + WooCommerce 11.2.0: 24/24**.

`tests/run-wp-admin-tests.php` uses stand-in WordPress functions; the real-WP
suites are the ones that prove behaviour inside WordPress itself.
