=== Laqi Product Data Checks ===
Contributors: laqilogistics
Tags: woocommerce, product data, shipping, weight, catalog
Requires at least: 6.8
Tested up to: 7.1
Requires PHP: 7.4
Stable tag: 1.2.2
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html
WC requires at least: 7.1
WC tested up to: 11.1

Find the products that will cost you money when you ship them, including missing weights, missing dimensions, and no shipping class.

== Description ==

WooCommerce will happily publish a physical product with no weight, and nothing warns you. The bill arrives somewhere else entirely, weeks later.

* **Shipping is undercharged**, quietly, on every order containing that product. You pay the difference and never know which order caused it.
* **Some carrier integrations stop calculating rates altogether** when a product has no weight. The customer reaches checkout, sees no shipping options at all, and leaves.
* **Variations are the worst case**, because a variation with no weight is invisible from the products list.

Laqi Product Data Checks scans your catalogue and tells you which products are configured in a way that will fail or cost money at fulfilment. It groups the results so you can act on them instead of facing a wall of rows.

**What it checks**

* Products that are shipped and have no weight.
* Variations with no weight of their own and no parent weight to inherit.
* Incomplete dimensions. This check only runs on stores that set dimensions somewhere, so a store pricing purely by weight is never told its whole catalogue is broken.
* Shipped products with no shipping class. This check only runs where the rates charge per shipping class and set no cost for items without one, which is the configuration where an unclassified product adds nothing at all to the rate and every order containing it is undercharged.
* Weights far beyond anything a courier carries, which is usually a misplaced decimal point or the wrong unit.
* Products published with no price, which WooCommerce quietly makes unbuyable.
* Variable products with no variations, or none a customer can buy.
* Stock management switched on with no quantity ever set.
* Virtual products carrying a weight or dimensions, which usually means the product type is wrong and no shipping has ever been charged for them.
* Missing SKUs. This check only runs on stores that use SKUs, and is gated separately for products and variations because identifying parents but not variations is a normal way to work.
* Missing images. This check only runs on stores that use product images. Variations showing their parent's image are never reported.
* Downloadable products with no file attached, which WooCommerce will sell quite happily and deliver nothing for.
* A stock quantity that contradicts the in-stock flag. The two are stored separately, and an import that writes one without the other leaves the product either overselling or unsellable.
* Missing GTIN, UPC, EAN or ISBN. This check only runs on stores that record them.
* Products still filed only under the default category. This check only runs on stores that file things elsewhere.

Under **Checks** every rule states whether it looks at products, variations, or both, so you always know which screen a finding will send you to.

Checks skip products that are never handed to a carrier, including external/affiliate products, grouped products, and variable parents whose variations are checked instead.

**When a finding is not a problem**

* **Dismiss** it. That product stops being reported for that check, and a rescan never undoes the dismissal. The scan decides whether a fault is still there, not whether you care about it.
* **Switch the check off** entirely if it does not apply to how your store works. Existing findings are kept, so switching it back on restores them rather than starting over.

**How it reports**

* One screen under WooCommerce, grouped by check and then by product.
* A product with two hundred variations missing a weight is **one row**, not two hundred.
* A variation that correctly inherits its parent's weight is never reported.
* Every check says what it costs you, not just what is missing.
* Findings are remembered between scans, and each product shows how long its fault has been standing, so a problem that comes back reads as a regression rather than as something this scan turned up.

**How it scans**

Scans run in the background through Action Scheduler, in bounded batches, walking the catalogue by product ID. Large catalogues are the ones most likely to have data problems, so scanning has to work on them.

**Privacy and external services**

This plugin does not contact Laqi Logistics or any third party. It reads your products, stores its findings in your own database, and sends nothing anywhere.

== Installation ==

1. Install and activate WooCommerce.
2. Install and activate Laqi Product Data Checks.
3. Go to **WooCommerce > Product Data Checks** and choose **Scan now**.

== Frequently Asked Questions ==

= Does it change my products? =

No. It only reads them. Every fix is made by you, in the normal WooCommerce product editor.

= Why is a variation with no weight not reported? =

Because WooCommerce falls back to the parent product's weight when a variation has none of its own. That variation will ship correctly, so reporting it would be wrong. Only variations with no weight anywhere are listed.

= Does zero count as a weight? =

No. A zero-weight parcel is refused by carriers exactly as a missing one is, and zero is what importers write when a field was blank.

= Will scanning slow down my shop? =

No. Scans run in the background in small batches, not during page requests.

= A finding is wrong for my store. Can I get rid of it? =

Yes, two ways. Dismiss the individual product, and it stops being reported for that check. A rescan never brings it back because the scan decides whether a fault is still there, not whether you care about it. Or switch the whole check off under **Checks** if it does not apply to how you work.

= If I switch a check off and back on, do I lose its history? =

No. Findings are kept while a check is off, so switching it back on restores them rather than starting over.

= What happens to my findings if I delete the plugin? =

They are deleted with it. Findings are derived data, so scanning again recreates all of them.

== Screenshots ==

1. A completed scan, with findings grouped by check. Each row explains the problem, its severity, and how many products it affects. A variable product counts once, not once per variation.
2. One check opened. Each variable product is a single row carrying the number of affected variations, with Edit and Dismiss beside it, rather than one row per variation.
3. The Checks screen. All fifteen checks, what each one applies to, its severity, and the categories it can be told to skip. Switch one off and its existing findings are kept for when you switch it back on.

== Changelog ==

= 1.2.2 =
* Reads the category exclusions on the Checks screen through the rule keys this plugin registers, rather than through keys taken from the request, and reads their values as integers. Nothing from the form is read before it is sanitised.

= 1.2.1 =
* Sanitises the posted form before handing it to the two hooks an add-on uses to save its own fields. The whole posted form was passed on untouched, on the reasoning that a listener sanitises the fields it owns. That is not good enough: a listener is code this plugin cannot vouch for, and input is sanitised before it is passed on, not after.
* Reads the category exclusions on the Checks screen as the term ids they are, rather than as free text validated later.

= 1.2.0 =
* Every product listed under a check now shows how long it has been failing it. A fault that was fixed and has come back is dated from when it first appeared, which is what makes it read as a regression rather than as something new.
* Adds a shipping class check. A store that charges per shipping class and sets no cost for items without one adds nothing to the rate for an unclassified product, so every order containing one is undercharged and nothing at checkout says so. The check runs only on stores configured that way, and only once some product carries a class.

Earlier releases are documented in changelog.txt, included with the plugin.
