|
|
@@ -0,0 +1,356 @@
|
|
|
+# Plugin Revision — v1.4.1 (Fresh-Eye Analytical Review)
|
|
|
+
|
|
|
+> **Status as of 1.4.2 (2026-05-12):** every in-scope finding below has been resolved in plugin version **1.4.2**. Each finding now carries an inline status marker — ✅ resolved, ⏸ deliberately deferred. See the README changelog for the corresponding code changes.
|
|
|
+
|
|
|
+**Scope:** Independent read-only analytical pass over the v1.4.1 codebase. Read with deliberately fresh eyes — not just as "did the 1.4.0 fixes land", but "what would I flag if I had never seen this plugin before". Findings include items that were latent in v1.3.2 and survived both prior reviews.
|
|
|
+
|
|
|
+**Reviewer date:** 2026-05-12
|
|
|
+**Target environment:** WordPress 6.9.4, WooCommerce 10.7.0 (HPOS enabled), PHP 7.2+.
|
|
|
+**Files reviewed:** all PHP under `includes/`, the bootstrap `studiou-wc-ord-print-statuses.php`, and the markdown docs.
|
|
|
+**Verification limitations:** No live PHP/WP/WC available. Findings rest on source reading and reasonable WP/WC behavioural assumptions for the target stack.
|
|
|
+
|
|
|
+Severity reflects user-visible or correctness impact, not implementation effort.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 1. Critical
|
|
|
+
|
|
|
+*(None. All 1.3.2 critical findings and the new 1.4.0 criticals are resolved in 1.4.1.)*
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 2. High — newly discovered bugs and latent defects
|
|
|
+
|
|
|
+### 2.1 `Custom_Columns_Manager::format_meta_date()` applies the WP timezone offset **twice** — column display can be hours off — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: parse the stored value with `DateTimeImmutable(..., wp_timezone())` and format via `wp_date()`. Eliminates the double-offset drift while keeping locale-aware formatting.
|
|
|
+
|
|
|
+`class-custom-columns-manager.php:50-59`:
|
|
|
+
|
|
|
+```php
|
|
|
+private function format_meta_date($value) {
|
|
|
+ if (empty($value)) { return '—'; }
|
|
|
+ $timestamp = strtotime($value);
|
|
|
+ if (!$timestamp) { return '—'; }
|
|
|
+ return esc_html(date_i18n(get_option('date_format') . ' ' . get_option('time_format'), $timestamp));
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+Two issues compound:
|
|
|
+
|
|
|
+1. `strtotime($value)` interprets the input string in the **PHP server timezone**.
|
|
|
+2. `date_i18n(..., $timestamp)` formats the UNIX timestamp in the **WordPress site timezone**.
|
|
|
+
|
|
|
+`$value` is a MySQL datetime string written by `current_time('mysql')`, which is already in WP local time. The intended round-trip is: read string → display unchanged. Instead the pipeline does: parse string *as if* server-local → convert to UTC → format in WP-local. On any host where the PHP server timezone differs from the WP timezone (e.g., UTC server + `Europe/Prague` site), the displayed value drifts by the offset.
|
|
|
+
|
|
|
+**Concrete example** with server UTC, WP `Europe/Prague` (UTC+2 in summer):
|
|
|
+
|
|
|
+- Stored meta: `2026-05-12 16:30:00` (intended Prague-local, equivalent to 14:30 UTC).
|
|
|
+- `strtotime('2026-05-12 16:30:00')` → `1747066200` (16:30 UTC).
|
|
|
+- `date_i18n('Y-m-d H:i', 1747066200)` in WP-Prague → `2026-05-12 18:30`.
|
|
|
+- **User sees `18:30` for a record they expect to read `16:30`.**
|
|
|
+
|
|
|
+This is the same class of bug v1.4.0 review §2.2 identified for `Order_Fields_Manager`. That class was rewritten to pure string reformatting; `Custom_Columns_Manager` was missed. The fix is to convert the same way:
|
|
|
+
|
|
|
+```php
|
|
|
+if (preg_match('/^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})/', $value, $m)) {
|
|
|
+ return esc_html($m[1] . ' ' . $m[2]);
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+…or, if locale-aware formatting is required, build a `DateTimeImmutable` with `wp_timezone()` first:
|
|
|
+
|
|
|
+```php
|
|
|
+$dt = DateTimeImmutable::createFromFormat('Y-m-d H:i:s', $value, wp_timezone());
|
|
|
+if ($dt) {
|
|
|
+ return esc_html(wp_date(get_option('date_format') . ' ' . get_option('time_format'), $dt->getTimestamp()));
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+The "To Print Date" and "In Print Date" columns are the public face of this plugin in the orders list — this bug is user-visible on every page load.
|
|
|
+
|
|
|
+### 2.2 Variation attribute join uses `terms.name` while WC stores the **slug** in `attribute_pa_format` — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: join condition changed to `product_variations_name.slug = product_variations_attr.meta_value`. The SELECT keeps `MIN(product_variations_name.name)` so the displayed value remains the human-readable name.
|
|
|
+
|
|
|
+`class-db-manager.php:60-64`:
|
|
|
+
|
|
|
+```php
|
|
|
+LEFT JOIN `{$wpdb->postmeta}` `product_variations_attr`
|
|
|
+ ON `product_variations_attr`.`post_id` = `product_variations`.`ID`
|
|
|
+ AND `product_variations_attr`.`meta_key` = 'attribute_pa_format'
|
|
|
+LEFT JOIN `{$wpdb->terms}` `product_variations_name`
|
|
|
+ ON `product_variations_name`.`name` = `product_variations_attr`.`meta_value`
|
|
|
+```
|
|
|
+
|
|
|
+WC variation postmeta for attribute taxonomies (`attribute_pa_*`) stores the **term slug**, not the term name. The join is on `terms.name = meta_value`. This works only when name and slug coincide (e.g., a term where both are `"A4"`). In stores where the human-readable name diverges from the slug (e.g., `name="Velký A4"`, `slug="a4"`), the join returns nothing and `prod_var_type` is `NULL` for that line item.
|
|
|
+
|
|
|
+CLAUDE.md §"Version 1.3.2" describes the 1.3.2 fix as *"Join with wp_terms using the slug field (matching against meta_value) instead of term_id"*. The implementation uses `name`, not `slug`. This is either:
|
|
|
+
|
|
|
+- A documentation/code disagreement (CLAUDE.md is wrong about which column is used), or
|
|
|
+- A real latent defect that has been hidden so far because Studiou's `pa_format` terms happen to have matching name+slug.
|
|
|
+
|
|
|
+The correct join is `product_variations_name.slug = product_variations_attr.meta_value`. If display of the human-readable name (rather than the slug) is desired, leave the column as `MIN(product_variations_name.name)` but switch the join key.
|
|
|
+
|
|
|
+### 2.3 `MIN(order_products.product_qty)` silently undercounts duplicated line items — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: replaced with `SUM(order_products.product_qty)`. A single-row group still returns the same value; duplicated line items now sum correctly.
|
|
|
+
|
|
|
+`class-db-manager.php:46-52` SELECTs:
|
|
|
+
|
|
|
+```sql
|
|
|
+MIN(`order_products`.`product_qty`) AS `qty`
|
|
|
+```
|
|
|
+
|
|
|
+…with `GROUP BY orders.id, order_products.product_id, order_products.variation_id`.
|
|
|
+
|
|
|
+`wc_order_product_lookup` is keyed per **order line item**, not per (order × product × variation). A single order can legitimately contain two line items for the same variation — most commonly when a customer adds the product, edits the cart, and re-adds the same variation rather than incrementing the quantity. WC merges these on display but the lookup table preserves the original line items.
|
|
|
+
|
|
|
+When that happens, the `GROUP BY` collapses both line items to one row and `MIN(qty)` returns one of the two quantities, *not* their sum. The print shop receives an under-quantity; some prints are missed.
|
|
|
+
|
|
|
+The semantically correct aggregation is `SUM(order_products.product_qty)`. `MIN()` is only safe when each group is guaranteed to have exactly one row — which the 1.4.1 review assumed without verifying.
|
|
|
+
|
|
|
+`SUM` over a single-row group equals that row's value, so this is a forward-compatible fix.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 3. Medium — pre-existing issues that didn't surface in earlier reviews
|
|
|
+
|
|
|
+### 3.1 `normalise_csv_date()` formats with server timezone, breaking consistency with `current_time('mysql')` — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: `date()` replaced with `wp_date()`. Imported `external_ref_ord_date` is now stored in WP local time, matching every other meta key.
|
|
|
+
|
|
|
+`class-db-manager.php:301-312`:
|
|
|
+
|
|
|
+```php
|
|
|
+$timestamp = strtotime($raw);
|
|
|
+…
|
|
|
+return date('Y-m-d H:i:s', $timestamp);
|
|
|
+```
|
|
|
+
|
|
|
+`date()` formats in the **server** timezone. Every other write site uses `current_time('mysql')` which is in WP timezone. The import-side `external_ref_ord_date` is therefore stored in server timezone, while values written from elsewhere are in WP timezone.
|
|
|
+
|
|
|
+Today this is hidden because:
|
|
|
+- The Print Information panel reads the value as an opaque string (no timezone interpretation).
|
|
|
+- The orders-list column has its own bug (§2.1) that adds a second offset.
|
|
|
+
|
|
|
+Once §2.1 is fixed, this asymmetry becomes user-visible. Either replace with `wp_date('Y-m-d H:i:s', $timestamp)` or construct via `DateTimeImmutable` in `wp_timezone()`.
|
|
|
+
|
|
|
+### 3.2 `wc_get_order($row['order_no'])` fails silently when the site uses non-numeric order numbers — ⏸ documented, no code change
|
|
|
+
|
|
|
+Resolution path: integrating a specific custom-order-number plugin is out of scope without knowing which one Studiou might adopt. Instead, the README now explicitly states that `order_no` in CSVs is the WC internal order ID, and the "Known limitations" section flags that custom-order-number plugins are not integrated. The export round-trip is internally consistent (the print shop echoes back what we sent).
|
|
|
+
|
|
|
+`class-db-manager.php:171, 209`. WC's `wc_get_order()` casts its argument to int when given a string. A custom-order-number plugin (e.g., "Sequential Order Numbers") that emits values like `ORD-12345` would have `wc_get_order('ORD-12345')` return `false` because `(int) 'ORD-12345' === 0`.
|
|
|
+
|
|
|
+The current Studiou install presumably does not use such a plugin, but:
|
|
|
+- The export query returns `orders.id` as `order_no` — the WC internal ID, **not** any displayed/custom order number.
|
|
|
+- The print provider then echoes back what they received, so the round-trip is consistent.
|
|
|
+- But this is undocumented. README §"CSV formats" calls the column `order_no` without clarifying that it is the WC order ID, not the customer-facing order number.
|
|
|
+
|
|
|
+Risk: when Studiou eventually installs a sequential-numbers plugin and customer-facing order numbers diverge from IDs, the import contract silently breaks.
|
|
|
+
|
|
|
+Suggested mitigation: document the contract; optionally support lookup-by-meta-key as a fallback (`wc_orders_table_query`/`wc_get_orders` with a meta query on the custom-number meta key).
|
|
|
+
|
|
|
+### 3.3 `INNER JOIN` on `product_variations` excludes orders containing simple (non-variable) products — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: changed to `LEFT JOIN`. Simple products now appear in the export with blank `prod_var`/`prod_var_type`. README "Known limitations" calls out the implication.
|
|
|
+
|
|
|
+`class-db-manager.php:58-59`:
|
|
|
+
|
|
|
+```sql
|
|
|
+JOIN `{$wpdb->posts}` `product_variations`
|
|
|
+ ON `product_variations`.`ID` = `order_products`.`variation_id`
|
|
|
+```
|
|
|
+
|
|
|
+When the order line item is a non-variable product, `variation_id = 0` and there is no matching `posts` row. The `JOIN` (inner) eliminates that row entirely.
|
|
|
+
|
|
|
+If the print shop sometimes ships products that aren't configured as variations in WC, those products vanish from the CSV — silently. The plugin appears designed only for variable products (`prod_var`, `prod_var_type` columns), so this is *intentional* but undocumented. A reviewer auditing this code for the first time would not be able to tell.
|
|
|
+
|
|
|
+Either convert to `LEFT JOIN` and `COALESCE(product_variations.post_title, products.post_title)`, or document the design constraint explicitly.
|
|
|
+
|
|
|
+### 3.4 `SET SESSION group_concat_max_len = 65535` leaks to other queries on the same connection — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: switched to `SET SESSION group_concat_max_len = GREATEST(@@SESSION.group_concat_max_len, 65535)` — never lowers a higher pre-existing value, only raises it to at least 64 KB.
|
|
|
+
|
|
|
+`class-db-manager.php:37`. `SET SESSION` is connection-scoped. In WP's default (non-persistent) MySQL connections, the connection lifetime equals the request lifetime, so the side effect is bounded. With a persistent connection pool (`mysqli.allow_persistent=1` plus a managed pool), the modified value carries into other requests reusing the same connection. Highly unlikely to break anything (the limit only goes up), but worth knowing.
|
|
|
+
|
|
|
+A cleaner pattern is `$wpdb->query("SET @@SESSION.group_concat_max_len = GREATEST(@@SESSION.group_concat_max_len, 65535)")` or wrapping the export in `SET …; SELECT …; SET @@SESSION.group_concat_max_len = DEFAULT;`. Cost/benefit: cosmetic for the current deployment.
|
|
|
+
|
|
|
+### 3.5 `images.guid` is used as the image URL, but WP's codex explicitly advises against it — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: the export now selects the `_thumbnail_id` and resolves URLs via `wp_get_attachment_url()` in PHP after the query. The `wp_posts` join on the attachment table was dropped. Offloaded-media plugins, multisite uploads paths, and the `wp_get_attachment_url` filter chain are now respected.
|
|
|
+
|
|
|
+`class-db-manager.php:50, 68-70` selects and joins `wp_posts.guid` for the variant thumbnail URL. The WordPress codex documents `guid` as "not necessarily a URL" and warns against using it for that purpose. The canonical lookup is `wp_get_attachment_url($attachment_id)`, which respects:
|
|
|
+
|
|
|
+- Multisite per-blog uploads paths
|
|
|
+- Filters (`wp_get_attachment_url`, `upload_dir`)
|
|
|
+- Image stored in S3 / offloaded media plugins
|
|
|
+
|
|
|
+Pure SQL can't call `wp_get_attachment_url`. The pragmatic alternative is to select the `_thumbnail_id` (already joined as `product_meta.meta_value`) and resolve URLs in PHP for each row before sending the CSV. The cost is one PHP call per line item — negligible for export-volume row counts.
|
|
|
+
|
|
|
+This is a pre-existing concern from v1.3.2 that none of the earlier reviews surfaced.
|
|
|
+
|
|
|
+### 3.6 Server-side file extension / MIME check missing on imports — ✅ resolved in 1.4.2 (extension)
|
|
|
+
|
|
|
+Resolution: `validated_upload()` now checks `pathinfo($file['name'], PATHINFO_EXTENSION) === 'csv'` after the existing `$_FILES[...]['error']` and `is_uploaded_file()` checks. A stricter MIME-type sniff was considered overkill given that `parse_csv` itself only reads bytes — no execution path exists for an uploaded file.
|
|
|
+
|
|
|
+`class-import-manager.php:134-156` validates `$_FILES[...]['error']` and `is_uploaded_file()`, but does not check the file extension or MIME type. The `accept=".csv,text/csv"` HTML attribute is client-side only.
|
|
|
+
|
|
|
+Today this is not exploitable: `parse_csv` simply reads bytes through `fgetcsv` and returns rows. A misnamed binary file produces an empty parse result and the "could not be parsed" notice. But defense-in-depth would be:
|
|
|
+
|
|
|
+```php
|
|
|
+$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
|
|
|
+if ($ext !== 'csv') { /* notice + abort */ }
|
|
|
+```
|
|
|
+
|
|
|
+Cheap and good hygiene.
|
|
|
+
|
|
|
+### 3.7 `wc_order_product_lookup` dependency is implicit — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: `get_orders_for_printing()` now pre-checks the table with `SHOW TABLES LIKE …` and, if it is missing, emits a clear admin notice ("Export requires the WooCommerce analytics lookup table. Enable WooCommerce Analytics or contact support.") and returns an empty result instead of letting the SQL error surface obliquely.
|
|
|
+
|
|
|
+`class-db-manager.php:53-54` joins `{$wpdb->prefix}wc_order_product_lookup`. This table is part of WC analytics and is created by WC by default — but it can be absent on:
|
|
|
+
|
|
|
+- Very old WC installs that pre-date the analytics module
|
|
|
+- Installs where the user explicitly disabled the WC analytics feature (it can be turned off in WC 4.0+)
|
|
|
+- Newly migrated databases where the lookup-table sync hasn't run yet
|
|
|
+
|
|
|
+When the table is missing the export query fails with a SQL error; the failure is logged and the bulk action emits "no exportable items" but doesn't tell the operator *why*. Either pre-check `$wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}wc_order_product_lookup'")` once at boot, or surface the SQL error message in the warning notice when it is "table doesn't exist".
|
|
|
+
|
|
|
+### 3.8 HPOS-search hook name `woocommerce_order_table_search_query_meta_keys` is still unverified against WC 10.7.0 — ⏸ deferred (verification task)
|
|
|
+
|
|
|
+Same status as in the v1.4.0 review — no live verification possible from this environment. README "Known limitations" surfaces this so operators can confirm against the running site.
|
|
|
+
|
|
|
+Carried over from `revise-1.4.0.md` §2.1. No live WC available; the filter name has not been visually confirmed in WC 10.7.0 source. If the filter has been renamed, the legacy `woocommerce_shop_order_search_fields` will still keep the feature working under legacy CPT, but HPOS users will lose it again.
|
|
|
+
|
|
|
+This remains the single most important verification ask before release-tagging 1.4.1.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 4. Low — style and minor
|
|
|
+
|
|
|
+### 4.1 `parse_csv` uses error-suppression `@fopen` — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: `fopen()` is now called without `@`; failures are logged via `UtilsLog::log()`.
|
|
|
+
|
|
|
+`class-db-manager.php:234` — `@fopen()` swallows the underlying warning. Replace with `fopen($path, 'r')` plus explicit error logging on `false`, and add a `clearstatcache()` if relevant. WordPress's general style is to avoid `@`.
|
|
|
+
|
|
|
+### 4.2 `STUDIOU_WC_OPS_VERSION` / `STUDIOU_WC_OPS_FILE` remain defined but unused inside the plugin — ⏸ left as-is
|
|
|
+
|
|
|
+`studiou-wc-ord-print-statuses.php:22-23`. Acknowledged in `revise-1.4.0.md` §3.6 / §4.6 — left in place for external consumers. No action.
|
|
|
+
|
|
|
+### 4.3 `handle_status_transitions` doesn't clear meta on transition *out* of a print status — ⏸ accepted as designed
|
|
|
+
|
|
|
+`class-order-status-manager.php:79-94` stamps `to_print_date` / `in_print_date` on entry, but never clears them on exit (e.g., to-print → cancelled). The historical timestamp is preserved — usually desired. Worth documenting as the deliberate design choice if a future contributor wonders.
|
|
|
+
|
|
|
+### 4.4 `woocommerce_process_shop_order_meta` hook coverage under HPOS — ⏸ left as-is (compat shim still present in WC 10.7.0)
|
|
|
+
|
|
|
+`class-order-fields-manager.php:31` hooks `woocommerce_process_shop_order_meta`. WC 8.x added a compatibility shim that fires this hook on HPOS as well, but the canonical HPOS save hook is `woocommerce_after_order_object_save`. If WC ever deprecates the compatibility shim, manual edits to the Print Information panel will stop persisting on HPOS-only setups. Worth a forward-looking note.
|
|
|
+
|
|
|
+### 4.5 `private $custom_statuses` initialised lazily but `=== null` check is fragile under any future strict-type migration — ⏸ left as-is (no typed-property refactor planned)
|
|
|
+
|
|
|
+`class-order-status-manager.php:7, 17-18`. If the property is later typed as `array $custom_statuses` (PHP 7.4 typed properties), an uninitialised typed property emits an `Error` on read — `=== null` would never reach. Cosmetic concern; flag if/when typed-properties refactor happens.
|
|
|
+
|
|
|
+### 4.6 `generate_csv()` reads `(array) $data[0]` without re-guarding — ⏸ left as-is (caller guarantees non-empty)
|
|
|
+
|
|
|
+`class-bulk-actions-manager.php:113`. Defensive: caller already gates with `!empty($orders_data)`, so this is safe today. But the function would crash on direct invocation with an empty array. A one-line `if (empty($data)) return '';` would make it self-contained.
|
|
|
+
|
|
|
+### 4.7 Two `__('Import Protocols', …)` calls produce identical translatable strings — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: cached in a local `$title` variable before the `add_submenu_page` call.
|
|
|
+
|
|
|
+`class-import-manager.php:16-17` translates the same string twice (menu title and page title). Functionally identical, two lookups instead of one. Cosmetic.
|
|
|
+
|
|
|
+### 4.8 `register_post_status` still called under HPOS — ⏸ left as-is
|
|
|
+
|
|
|
+Carried over from `revise-1.4.0.md` §4.9. Harmless duplication, kept for WC admin chrome compatibility.
|
|
|
+
|
|
|
+### 4.9 `add_custom_order_statuses_to_list` silently no-ops when `wc-processing` is absent — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: an `$inserted` flag tracks whether the anchor was found; the fallback path appends the custom statuses at the end of the dropdown if not.
|
|
|
+
|
|
|
+`class-order-status-manager.php:54-65`. If a third party removes `wc-processing` from `wc_order_statuses`, our statuses are never inserted (the foreach finds no anchor). Highly unlikely in practice; a defensive fallback that appends at the end if `wc-processing` is missing would tighten this.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 5. Documentation drift
|
|
|
+
|
|
|
+### 5.1 CLAUDE.md describes the `attribute_pa_format` join as using `slug`; the code uses `name` — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: the code was changed to use `slug` (§2.2); CLAUDE.md's description now matches reality. The 1.3.2 changelog entry there gained a footnote pointing at this review.
|
|
|
+
|
|
|
+CLAUDE.md §"Version 1.3.2" `Recent Changes` paragraph says:
|
|
|
+
|
|
|
+> Join with `wp_terms` using the slug field (matching against meta_value) instead of term_id
|
|
|
+
|
|
|
+The actual SQL in `class-db-manager.php:64` joins on `terms.name = meta_value`. See §2.2 above — either fix the code or fix the doc, but the two should agree.
|
|
|
+
|
|
|
+### 5.2 README's CSV-import section doesn't define what `order_no` means — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: a callout above the CSV-format section explicitly states `order_no` is the WooCommerce internal order ID and describes the interaction with custom-order-number plugins.
|
|
|
+
|
|
|
+§3.2 above. README should explicitly say `order_no` is the WC internal order ID, and explain the interaction (or lack thereof) with custom-order-number plugins.
|
|
|
+
|
|
|
+### 5.3 Deferred items in `revise-1.4.0.md` should appear in the project's "Known limitations" section of README — ✅ resolved in 1.4.2
|
|
|
+
|
|
|
+Resolution: README has a new "Known limitations" section listing HPOS search-filter verification, variable-products-only design, WC Analytics dependency, block-based order edit screen, no multisite header, no uninstall cleanup, custom-order-number plugin gap, hardcoded `attribute_pa_format`, and the `group_concat_max_len` session-scoped bump.
|
|
|
+
|
|
|
+Currently the README changelog mentions them only in passing under 1.4.1. New readers of the README don't get the same heads-up that a contributor reading the revise doc does. A short "Known limitations" section in the README would consolidate:
|
|
|
+
|
|
|
+- HPOS search filter verification still pending on WC 10.7.0
|
|
|
+- No multisite `Network:` header
|
|
|
+- No uninstall cleanup
|
|
|
+- Block-based order edit screen unsupported
|
|
|
+- Optimised for variable products only (§3.3)
|
|
|
+- Reliance on WC analytics' `wc_order_product_lookup` table (§3.7)
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 6. Spec compliance recap (against `docs/instructions.txt`)
|
|
|
+
|
|
|
+| Spec item | Status as of 1.4.1 |
|
|
|
+|-----------|--------------------|
|
|
|
+| 1. Custom statuses after `wc-processing` | ✅ |
|
|
|
+| 2. Custom order fields | ✅ (plus `external_ref_ord_date` extension) |
|
|
|
+| 3. Search by `external-ref-ord-no` | ⏸ resolved in code, HPOS filter name pending verification |
|
|
|
+| 4. Three bulk actions | ✅ |
|
|
|
+| 5. Three custom columns | ✅ (display bug §2.1 affects the date columns) |
|
|
|
+| 6. Import InPrint with DISTINCT | ✅ |
|
|
|
+| 7. Import Delivered with DISTINCT | ✅ |
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 7. Risk-prioritised summary
|
|
|
+
|
|
|
+Ordered by user-visible severity. Status as of **1.4.2**:
|
|
|
+
|
|
|
+1. **§2.1 Column-display timezone double-shift.** ✅ resolved.
|
|
|
+2. **§2.3 `MIN(qty)` undercount with duplicate line items.** ✅ resolved (`SUM(qty)`).
|
|
|
+3. **§2.2 Variation join on `terms.name` instead of `terms.slug`.** ✅ resolved.
|
|
|
+4. **§3.1 `normalise_csv_date` server-timezone vs WP-timezone asymmetry.** ✅ resolved (`wp_date()`).
|
|
|
+5. **§3.8 HPOS search filter unverified.** ⏸ deferred — still pending live verification on WC 10.7.0.
|
|
|
+6. **§3.5 `images.guid` as the image URL.** ✅ resolved (`wp_get_attachment_url()`).
|
|
|
+7. **§3.7 Implicit `wc_order_product_lookup` dependency.** ✅ resolved (pre-flight `SHOW TABLES`).
|
|
|
+8. **§3.3 Non-variable products silently absent from export.** ✅ resolved (`LEFT JOIN`).
|
|
|
+9. **§3.2 `wc_get_order($row['order_no'])` for non-integer order numbers.** ⏸ documented, no code change.
|
|
|
+10. **§3.6 No server-side CSV extension/MIME check.** ✅ resolved (extension only).
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 8. Resolution status of prior reviews
|
|
|
+
|
|
|
+- **`revise-1.3.2.md`:** 26 of 30 findings resolved in 1.4.0. 3 deferred (no uninstall, no `Network:`, cosmetic README). 1 (HPOS search) resolved in code, verification pending. **No regressions detected.**
|
|
|
+- **`revise-1.4.0.md`:** all in-scope findings resolved in 1.4.1 per its own per-item status markers. **One regression remained latent — see §2.1 of this review.** §2.2 was the documented Order_Fields fix; the parallel issue in Custom_Columns_Manager was missed.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 9. Out of scope
|
|
|
+
|
|
|
+- Live behaviour testing against a running WP 6.9.4 / WC 10.7.0 install.
|
|
|
+- Performance profiling of the export query on large order volumes (the GROUP BY + 4× LEFT JOIN is non-trivial; needs EXPLAIN against production data).
|
|
|
+- Behavioural verification on the WC 10.7.0 block-based order edit screen.
|
|
|
+- `.po`/`.mo` correctness — translations were verified for source-side coverage only.
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+*End of review.*
|