|
|
3 minggu lalu | |
|---|---|---|
| .. | ||
| docs | 3 minggu lalu | |
| includes | 3 minggu lalu | |
| languages | 3 minggu lalu | |
| CLAUDE.md | 3 minggu lalu | |
| README.md | 3 minggu lalu | |
| studiou-wc-ord-print-statuses.php | 3 minggu lalu | |
WordPress plugin that extends WooCommerce with a dedicated workflow for managing print orders sent to third-party print providers. It adds custom order statuses, tracking fields, CSV export, protocol-based CSV imports, and search/filter capabilities tailored to a print fulfillment pipeline.
studiou-wc-ord-print-statuses| Component | Minimum | Tested up to |
|---|---|---|
| WordPress | 5.8 | 6.9.4 |
| WooCommerce | 3.0 | 10.7.0 |
| PHP | 7.2 | — |
Compatible with WooCommerce High-Performance Order Storage (HPOS) — the plugin declares custom_order_tables compatibility and queries the HPOS wc_orders table directly.
studiou-wc-ord-print-statuses folder into wp-content/plugins/.No additional setup is required — custom statuses, fields, columns, and bulk actions are registered on activation.
Two new statuses are inserted in the WooCommerce status pipeline, right after processing:
| Slug | Label | Purpose |
|---|---|---|
wc-to-print |
Prepare to Printing | Order is queued and exported to the print shop. |
wc-in-print |
In Printing | Print shop has accepted the order. |
Status transitions automatically stamp the related metadata field (to_print_date / in_print_date) with the current server time.
A new Print Information panel is added to the order details screen with four editable fields:
to_print_date)in_print_date)external_ref_ord_no)external_ref_ord_date) — populated by InPrint protocol imports.external_ref_ord_delivered)Available in the Bulk actions dropdown on WooCommerce → Orders:
wc-to-print.wc-to-print and stamps to_print_date.wc-in-print and stamps in_print_date.Three columns are appended after Total:
The standard WooCommerce Search orders input also matches the external_ref_ord_no meta value — no separate input is required. Works under both legacy CPT storage and HPOS (woocommerce_shop_order_search_fields and woocommerce_order_table_search_query_meta_keys filters).
Each product line item on an order carries its own print status independently of the order's status:
| Status | Meaning |
|---|---|
pending-print |
Initial. Waiting for the print provider. |
in-print |
Currently being printed. |
done-print |
Printed. |
skip-print |
Skipped — won't be printed (operator decision, exclusion). |
The status is editable per item from the Items table on the order edit screen via a <select> in the new Print Status column. Non-product line items (shipping, fees, coupons, refunds) don't carry a print status and show —.
Coupling rules between order status and item statuses (one-way, order → items):
| Order transition | Effect on product items |
|---|---|
→ wc-in-print |
pending-print items advance to in-print. Other statuses untouched. |
→ wc-completed |
Gated, not propagating. Blocked if any product item is still pending-print or in-print. |
| All other order transitions | No automatic item change. |
Item-level status changes never propagate back to the order. The operator drives the order status independently — the only constraint is the completion guard.
Bulk "Change status to Completed" (HPOS Orders list only, 1.5.1+): when you run WooCommerce's own Change status to Completed bulk action on the Orders screen (wp-admin/admin.php?page=wc-orders), the plugin first advances every still-pending-print/in-print item on each selected order to done-print, then lets WooCommerce complete the order — so the completion guard is satisfied instead of blocking. This is scoped strictly to that one bulk action on that one screen: single-order completion from the order-edit dropdown, programmatic/REST changes, and every other bulk action keep the standard guard behaviour. Orders already in a terminal status (completed/cancelled/refunded/failed) are not advanced — already-completed orders are left untouched, and terminal orders with unresolved items are still blocked by the guard.
Protocol imports interact with this feature:
wc-in-print; items propagate automatically via the rule above (no special code in the importer).pending-print/in-print item to done-print before flipping the order to wc-completed, so the completion guard is satisfied. The summary notice tells the operator how many items were auto-advanced.The CSV export adds a prod_print_status column reflecting each line item's current status.
A WooCommerce → Import Protocols submenu exposes two CSV upload forms:
wc-in-print and the external reference number / date are saved.wc-completed and the delivery timestamp is recorded.Both importers require the manage_woocommerce capability and are protected by WordPress nonces.
About
order_no: theorder_nocolumn written to the export and read by both imports is the WooCommerce internal order ID (wp_wc_orders.idunder HPOS,wp_posts.IDunder legacy CPT). It is not the user-facing order number produced by Sequential Order Numbers and similar plugins. If you install a custom-order-number plugin, the protocols still operate on the underlying order IDs — the print shop must echo back whatever value was sent in the export.
Generated file: prepare_to_printing_export.csv (UTF-8 with BOM — opens correctly in Excel on Windows).
Columns (in order):
order_no, prod_cat, prod_name, prod_var, prod_var_type, prod_img_url, qty, prod_print_status, email
prod_print_status (added in 1.5.0) carries the per-line-item print status — one of pending-print, in-print, done-print, or skip-print.
prod_var_type is sourced from the variation's attribute_pa_format postmeta, resolved against the wp_terms table.prod_cat is a comma-separated list (deduplicated) when a product belongs to multiple product_cat terms — the row itself is not duplicated.Headers are normalised on read: column names are lowercased and internal whitespace is collapsed to underscore. Both the original spec casing (Order No, ExternalOrder, ExternalOrderDate) and the documented lowercase form (order_no, externalorder, externalorderdate) work. Rows are also deduplicated by order_no before processing.
Required headers (any of the accepted casings):
order_no, externalorder, externalorderdate
Effect per row: order → status wc-in-print, sets in_print_date (now), external_ref_ord_no, external_ref_ord_date (parsed from the CSV). Orders in a terminal status (completed, cancelled, refunded, failed) are skipped with a per-row error notice.
Required header:
order_no
Effect per row: order → status wc-completed, sets external_ref_ord_delivered (now).
The plugin follows a manager-based pattern. The main bootstrapper (studiou-wc-ord-print-statuses.php) wires up specialised classes, each owning one slice of behaviour:
| Class | File | Responsibility |
|---|---|---|
Order_Status_Manager |
includes/class-order-status-manager.php |
Register custom statuses, handle transitions. |
Order_Fields_Manager |
includes/class-order-fields-manager.php |
Render and persist Print Information fields. |
Bulk_Actions_Manager |
includes/class-bulk-actions-manager.php |
Register and dispatch bulk actions. |
Custom_Columns_Manager |
includes/class-custom-columns-manager.php |
Inject extra columns into the Orders list. |
Import_Manager |
includes/class-import-manager.php |
Admin UI + handlers for protocol CSV imports. |
Order_Search_Manager |
includes/class-order-search-manager.php |
Extend order search to external reference no. |
Studiou_DB_Manager |
includes/class-db-manager.php |
All SQL and protocol-processing logic. |
UtilsLog |
includes/utils-log.php |
Lightweight logging helper (uses WP_DEBUG). |
The export query in Studiou_DB_Manager::get_orders_for_printing() joins wc_orders, wc_order_product_lookup, posts (products + variations), postmeta (variation attribute + thumbnail), terms, term_taxonomy, and term_relationships to assemble the print-shop payload in a single round-trip.
studiou-wc-ord-print-statuseslanguages/cs_CZ) — see docs/translations.txt for the EN→CS reference list.Use the standard WordPress functions (__(), _e(), _x(), _n_noop()) for any new strings.
UtilsLog::log($message) writes to the WordPress error log when WP_DEBUG is enabled (guarded as defined('WP_DEBUG') && WP_DEBUG — works for both boolean and integer truthy values). With WP_DEBUG_LOG on, log output ends up in wp-content/debug.log.UtilsLog::message($message, $type) queues a dismissible WordPress admin notice (info / success / warning / error) for the current user. Notices are stored in a per-user transient (60-second TTL) so they survive the redirect that follows bulk actions and imports. Used to report success counts, skipped orders, malformed CSV rows, etc.studiou-wc-ord-print-statuses/
├── studiou-wc-ord-print-statuses.php # Plugin bootstrap (header + main class)
├── includes/
│ ├── class-db-manager.php
│ ├── class-order-status-manager.php
│ ├── class-order-fields-manager.php
│ ├── class-order-search-manager.php
│ ├── class-bulk-actions-manager.php
│ ├── class-custom-columns-manager.php
│ ├── class-import-manager.php
│ └── utils-log.php
├── languages/ # .po / .mo translation files
├── docs/ # Project documentation (see below)
├── README.md # This file
└── CLAUDE.md # Guidance for Claude Code sessions
Reference documents live under docs/:
docs/readme.md — Previous user-facing readme (kept for history).docs/instructions.txt — Original feature specification used to scaffold the plugin.docs/translations.txt — EN → CS translation reference.docs/revise-1.3.2.md — Analytical review of v1.3.2 (all findings addressed in 1.4.0).docs/revise-1.4.0.md — Analytical review of v1.4.0 (all in-scope findings addressed in 1.4.1).docs/revise-1.4.1.md — Analytical review of v1.4.1 (all in-scope findings addressed in 1.4.2).docs/revise-1.4.2.md — Fresh-eye analytical review of v1.4.2 (all in-scope findings addressed in 1.4.3).docs/revise-1.4.3.md — Fresh-eye analytical review of v1.4.3 (all in-scope findings addressed in 1.4.4).docs/revise-1.4.4.md — Fresh-eye analytical review of v1.4.4 (mediums + lows addressed in 1.4.5; carried-over deferrals documented).Project-internal guidance for AI-assisted development is documented in CLAUDE.md.
woocommerce_order_table_search_query_meta_keys) has not been live-verified against WC 10.7.0. The legacy CPT filter is solid; the HPOS branch should be smoke-tested on the production site before relying on it.prod_var / prod_var_type. Simple (non-variable) products appear in the export as of 1.4.2 (via LEFT JOIN), but their variation fields are blank.wc_order_product_lookup, the WooCommerce Analytics lookup table. If WC Analytics is disabled or the table is missing, the export emits an admin notice and skips.woocommerce_admin_order_data_after_order_details. Behaviour on the WC 10.x block-based order edit screen has not been verified.Network: plugin header is set; activate per blog.to_print_date, external_ref_ord_no, etc.) remain in the database after plugin removal.attribute_pa_format taxonomy is hardcoded in the export SQL. Stores using a different attribute name will get a blank prod_var_type.group_concat_max_len is raised to 64 KB via SET SESSION per export — connection-scoped, harmless but worth knowing if you maintain custom queries on the same connection., as the field separator. Czech Excel defaults to ; as the list separator and may render the file as one column. Workaround: open with the Text Import wizard or set Windows regional list-separator to ,. Other consumers (LibreOffice Calc, Numbers, programmatic CSV parsers) handle the comma natively.post_status. The intended behaviour: show what was sold, even if the product has since been removed from the catalogue.terms.slug = postmeta.meta_value) is theoretically possible on databases that have been migrated across MySQL versions with mixed per-table collations. On standard utf8mb4_unicode_ci installs the join works fine. If ERROR 1267 (Illegal mix of collations) ever surfaces on the bulk export, the fix is to add an explicit COLLATE clause to the join condition.New feature: auto "Done Print" on the bulk "Change status to Completed" action. Scoped to WooCommerce's own Change status to Completed bulk action on the HPOS Orders list (wp-admin/admin.php?page=wc-orders) only.
Order_Item_Status_Manager::bulk_complete_advance_items(), hooked to handle_bulk_actions-woocommerce_page_wc-orders at priority 9 (before WooCommerce's own priority-10 mark_completed handler). For each selected non-terminal order it advances every still-pending-print/in-print product item to done-print before the status flip, so the completion guard passes instead of reverting.mark_completed bulk action on the HPOS Orders screen. Single-order completion from the order-edit dropdown, programmatic/REST/cron changes, the protocol imports, and all other bulk actions are unchanged — the standard completion guard still applies.completed/cancelled/refunded/failed are not advanced. Already-completed orders are left untouched (no silent item mutation); terminal orders with unresolved items remain blocked by the guard. Residual edge: a terminal order whose items are already all resolved is still completed by WooCommerce core (the guard never blocks on terminal status) — unchanged from 1.5.0.try/catch, so one failing order is logged and skipped without aborting the batch or WooCommerce's own completion pass. A per-order activity note records the advance; a single batch admin notice summarises totals, and a warning notice reports any failures.mark_*), the guard detects the same bulk-complete request context and advances the remaining items on the order it just completed — instead of reverting. It is priority-independent and runs on the same order object, and still reverts orders whose previous status was terminal, so the terminal-skip behaviour is preserved. Reuses the existing advance note (no new strings).docs/order-done-plan-01.md (§7 documents the fallback).New feature: per-order-item print status. Each product line item now carries its own print status independent of the order's status, with explicit coupling rules between the two.
pending-print (initial), in-print, done-print, skip-print. Stored as _print_status meta on each WC_Order_Item_Product. Missing meta defaults to pending-print (lazy — no migration needed for existing orders).<select> lets the operator change item status manually. Non-product items show —.wc-in-print transitions. The order being flipped to "In Printing" (via manual admin change, bulk action, or InPrint Protocol import) advances all pending-print items to in-print. done-print and skip-print items are untouched.wc-completed transitions. The order cannot complete while any product line item is still pending-print or in-print. Blocked transitions revert with an admin notice listing the count of unready items. Existing already-completed orders are unaffected.Studiou_DB_Manager::import_delivered_protocol() now calls advance_remaining_to_done() before flipping the order to wc-completed. Without this the completion guard would block every Delivered Protocol import. The import summary surfaces the total items advanced.prod_print_status column. Existing print-shop tooling that imports the CSV needs to add (or ignore) this column.docs/feature-order-item-status-analysis.md.Known limitations carried into 1.5.0: the new column renders on the classic order edit screen only — WC 10.x's block-based order edit screen (still feature-flagged) is not supported. Tracked as a phase-2 follow-up.
Defensive-hardening release addressing the medium and low findings in docs/revise-1.4.4.md:
Studiou_DB_Manager::normalise_csv_date() fast path now validates date and time ranges with checkdate() + hour/minute/second bounds. Structurally-valid but semantically-bogus inputs like "2026-13-45T25:99" fall through to the DateTimeImmutable fallback, which throws → caught → empty string returned. Mirrors the validation pattern already in Order_Fields_Manager::datetime_local_to_mysql.Import_Manager::emit_import_summary() now surfaces an info notice ("The CSV file contained no actionable rows (empty order_no column).") when both $updated and $errors are zero — closes the silent-import case where every CSV row had an empty order_no column.Studiou_DB_Manager::csv_escape() now ltrims before checking the first character. Cells like " =CMD" — which some spreadsheet apps strip and evaluate — are no longer slipping past the formula-injection defence.Studiou_DB_Manager::parse_csv() rejects CSVs whose normalised headers collide (e.g., two columns both ending up as order_no after whitespace/case normalisation). Previously array_combine silently overwrote, losing one column's data. Now logs and returns empty so the downstream import bails with the existing "could not be parsed" notice.bulk_set_print_status order-note selection lifted from a binary ternary to an explicit array(slug => note) map. Adding a third status in the future no longer risks inheriting the wrong note via the else branch.docs/revise-1.4.4.md updated with per-item status annotations. README "Known limitations" gains an entry for the theoretical MySQL collation mismatch on the slug join (§3.4 of the review).Deferred (low-impact, carried over): main plugin instance lifecycle (architectural, no functional impact),
woocommerce_missing_noticemid-request re-fire (edge case), collation mismatch (theoretical, no production occurrence).
Bug-fix release addressing the findings catalogued in docs/revise-1.4.3.md:
Studiou_DB_Manager::normalise_csv_date() was shifting timezone-less CSV dates by the WP timezone offset. WordPress sets the PHP timezone to UTC very early, so strtotime("2026-05-12 14:30") returned a UTC timestamp; wp_date() then formatted it in WP-local, producing a Prague-shifted "2026-05-12 16:30:00". Now uses pure-regex reformat for ISO-like inputs (no shift) and DateTimeImmutable(..., wp_timezone())->setTimezone(wp_timezone()) for the fallback path (handles explicit-timezone inputs correctly). No remaining strtotime calls in includes/ — the recurring timezone-bug pattern is now structurally eliminated.Studiou_DB_Manager::get_orders_for_printing() now pre-checks the HPOS wc_orders table parallel to the existing wc_order_product_lookup check. If HPOS is disabled the export bails with a clear admin notice ("Export requires WooCommerce HPOS … Enable High-Performance Order Storage …") instead of failing with a cryptic SQL error.bulk_set_print_status now passes an explanatory note to update_status — "Marked Prepare to Printing by bulk action." / "Marked In Printing by bulk action." — matching the audit-trail pattern the import paths use.Custom_Columns_Manager uses the WC_Order object directly when WC hands one to the column-content callback (HPOS path); under legacy CPT it memoises by ID per request so the same order isn't re-resolved across the three custom columns. Avoids 100–150 wc_get_order calls per 50-orders-per-page list.Import_Manager::emit_import_summary() suppresses the "0 orders updated successfully" notice when no rows changed — the per-error notice stands on its own. Matches the notify_moved_count pattern.call_user_func($cfg['handler'], $csv_data) → direct callable invocation. Tighter and slightly faster.before_woocommerce_init HPOS-compat declaration is now a named function (studiou_wc_ord_print_statuses_declare_hpos_compat). Can be remove_action-ed from outside.docs/revise-1.4.3.md updated with per-item status annotations.Bug-fix release addressing the findings catalogued in docs/revise-1.4.2.md:
import_delivered_protocol was missing the terminal-status guard that import_inprint_protocol had. A cancelled, refunded, or failed order in the delivered CSV was silently reactivated to completed. Now skipped with a per-row error notice. completed orders are still allowed through (re-running the same protocol is a benign no-op transition).manage_woocommerce (Administrator-only) while bulk actions only required edit_shop_orders (Shop Manager). Shop Managers can now run protocol imports — the two paths share the same capability requirement.Studiou_DB_Manager::table_exists() wraps the table name in $wpdb->esc_like(). The previous SHOW TABLES LIKE %s left the conventional _ in $wpdb->prefix as a SQL wildcard; the post-query equality comparison mitigated false positives but the safety relied on the wrong layer.=, +, -, @, \t, or \r is prefixed with a single quote. OWASP-recommended pattern; the leading quote is hidden by spreadsheet apps but disables formula evaluation.Order_Fields_Manager::datetime_local_to_mysql() now validates date and time ranges via checkdate() plus hour/minute/second bounds. Direct POST or DevTools-edited form values like 2026-13-45T25:99 no longer slip through to be stored as bogus MySQL datetimes.qty is now formatted in PHP — SUM(DECIMAL) returns "2.0000" from MySQL; the post-processing step normalises whole numbers to "2" and trims trailing zeros from decimals.update_status ("Marked In Printing by InPrint Protocol import." / "Marked completed by Delivered Protocol import."). The order activity feed reflects how the transition was triggered.UtilsLog::log — symmetric with the bulk-action path. Helps debug failed protocol imports.Import_Manager::emit_import_summary() deduplicates repeated error strings. A CSV with 100 malformed rows shows "100 errors occurred: Invalid row format in CSV." once, not 100 times concatenated.UtilsLog::render_notices() switched from wp_kses_post() to esc_html() — current notice messages are plain text, the tighter escape narrows the trust boundary at the API edge.Order_Status_Manager::set_order_processing_status() and its woocommerce_payment_complete hook. On modern WC the handler was effectively dead code — WC_Order::payment_complete() sets the order to processing (or completed for downloadable-only) before firing the action, so our handler was either a no-op same-state update or a deliberate skip. The corresponding translation string was removed too.docs/revise-1.4.2.md updated with per-item status annotations.Deferred (carried over from earlier reviews): live HPOS search-filter verification, block-based order edit screen,
Network:plugin header,uninstall.php, custom-order-number plugin integration.
Bug-fix release addressing the findings catalogued in docs/revise-1.4.1.md:
strtotime() interpreted the stored value in PHP server timezone, then date_i18n() formatted in WP timezone. On UTC-server / non-UTC-WP setups (e.g. UTC server + Europe/Prague site) the displayed time drifted by 1–2 hours. Now parsed explicitly as WP-local via DateTimeImmutable(..., wp_timezone()) and formatted with wp_date().wp_terms.name against the variation's attribute_pa_format postmeta, but WC stores the slug there, not the name. The join is now terms.slug = meta_value, while MIN(terms.name) still returns the human-readable display value.qty is now SUM(order_products.product_qty) instead of MIN(...). A single order can have multiple line items for the same (product, variation) when the customer added it twice without cart-merge; the previous query silently undercounted in that case.JOIN product_variations was inner — silently excluded orders containing simple (non-variable) products. Now a LEFT JOIN; simple products are included with blank prod_var/prod_var_type.prod_img_url no longer reads wp_posts.guid (which WP codex warns is not necessarily a URL). The export now selects _thumbnail_id and resolves URLs via wp_get_attachment_url() — respects offloaded-media plugins, multisite uploads paths, and wp_get_attachment_url filters.Studiou_DB_Manager::normalise_csv_date() now formats with wp_date() (WP timezone) instead of date() (server timezone), matching current_time('mysql') elsewhere.wc_order_product_lookup — if WC Analytics is disabled and the lookup table is missing, the export bails with a clear admin notice instead of failing with a cryptic SQL error.SET SESSION group_concat_max_len = GREATEST(@@SESSION.group_concat_max_len, 65535) — never lowers a higher session-scoped value another piece of code may rely on..csv extension check on protocol uploads (defense in depth on top of the existing is_uploaded_file() / $_FILES[...]['error'] checks).Order_Status_Manager::add_custom_order_statuses_to_list() falls back to appending the custom statuses at the end of the dropdown if wc-processing was removed by another plugin.parse_csv() drops error-suppression @fopen — failures are logged explicitly.order_no means in CSVs. CLAUDE.md corrected (the attribute_pa_format join uses slug, not name).docs/revise-1.4.1.md updated with per-item status annotations.Deferred (carried over): live HPOS search-filter verification,
Network:plugin header,uninstall.php, block-based order edit screen, custom-order-number plugin integration.
Bug-fix release addressing the findings catalogued in docs/revise-1.4.0.md:
ONLY_FULL_GROUP_BY mode (default in MySQL 5.7+ / MariaDB 10.3+). Non-aggregated columns (prod_name, prod_var, prod_var_type, prod_img_url, qty, email) are wrapped in MIN(); each (product_id, variation_id) group has exactly one value per column so MIN() is value-preserving.parse_csv() now strips a leading UTF-8 BOM. Re-importing a previously exported CSV (which ships with a BOM for Excel-on-Windows compatibility) no longer breaks header parsing.YYYY-MM-DD HH:MM:SS ↔ YYYY-MM-DDTHH:MM) instead of running them through strtotime()/date(). The previous timezone-shift bug (off by 1–2 hours on UTC servers with non-UTC WP timezones) is gone.set_order_processing_status() now records a single order note via update_status() instead of also calling add_order_note(). The order activity feed no longer duplicates the "payment received" line.output_csv() now aborts with wp_die() (and logs the offender) when headers_sent() is true, instead of streaming raw CSV bytes into a half-rendered HTML page.fputcsv/fgetcsv now pass an explicit empty-string escape character — no more PHP 8.4 deprecation warnings.Order_Fields_Manager::save_custom_order_fields() now checks current_user_can('edit_shop_order' / 'edit_shop_orders') before writing. Defense-in-depth on top of WC's own validation.Studiou_DB_Manager::get_orders_for_printing() raises group_concat_max_len to 64 KB before the export query, preventing silent prod_cat truncation for products in many categories.dedupe_by_order_no() is now case-insensitive — ord-12345 and ORD-12345 collapse to a single row.normalise_csv_date() returns an empty string (and logs) on parse failure instead of persisting the unparseable raw value.Import_Manager::run_import() is driven by a single protocol_dispatch() map (file field, nonce, DB handler per protocol). Every non-happy-path branch returns explicitly via redirect_back() for static-analysis friendliness.Order_Fields_Manager rendering and saving are driven from the datetime_fields() / text_fields() maps. Adding a new field is a single-map-entry change.docs/revise-1.4.0.md updated with per-item status annotations.Deliberately deferred (carried over from the 1.4.0 review): live verification of the HPOS search filter name (
woocommerce_order_table_search_query_meta_keys),Network:plugin header for multisite,uninstall.phpcleanup, and behaviour on the WC 10.x block-based order edit screen. None of these were addressed by 1.4.1.
Resolution of every finding catalogued in docs/revise-1.3.2.md. Highlights:
external_ref_ord_no consistently, no _ prefix), and the integration uses WooCommerce's own search filters (woocommerce_shop_order_search_fields for legacy CPT and woocommerce_order_table_search_query_meta_keys for HPOS). No separate UI input — typing the external order number into the standard Search orders box finds the order.Order_Status_Manager::handle_status_transitions() now writes timestamps via wc_get_order()->update_meta_data() instead of update_post_meta(). Print-date stamps are no longer silently lost under HPOS for non-bulk status transitions.order_no (per the original spec) and accept both the spec's title-case headers and the lowercase form documented in the README.charset=utf-8 header + leading \xEF\xBB\xBF). Czech diacritics render correctly in Excel on Windows.product_cat terms — categories are concatenated via GROUP_CONCAT(DISTINCT …) inside a GROUP BY per line item.$wpdb->prepare() with %d placeholders rather than relying on intval() for safety.woocommerce_payment_complete events no longer demote orders already in to-print or in-print back to processing. The guard extends to all print statuses plus completed.current_user_can('edit_shop_orders'). Imports keep their existing manage_woocommerce guard.completed, cancelled, refunded, failed) instead of silently reactivating them.UtilsLog::log() guard is now defined('WP_DEBUG') && WP_DEBUG (previously true === WP_DEBUG, which failed when WP_DEBUG was defined as integer 1).UtilsLog::message() is now implemented as a per-user transient that renders via the admin_notices hook. Bulk actions and imports now produce visible user feedback after the redirect.parse_csv() no longer truncates rows at 1000 bytes (passes 0 to fgetcsv), validates that the header row is non-empty, normalises header casing, and skips rows whose column count diverges from the header.$_FILES[...]['error'] and is_uploaded_file() server-side instead of relying solely on the client-side accept=".csv" attribute.external_ref_ord_date is parsed via strtotime() and stored in MySQL datetime format; if parsing fails the raw value is retained.external_ref_ord_date is now rendered in the Print Information panel on the order edit screen.STUDIOU_WC_OPS_VERSION and STUDIOU_WC_OPS_FILE constants for code-side version references.plugins_loaded priorities (5 and 10) so translations are guaranteed to load before any manager instantiates.// visualized leftover comments and the dead-code commented body of UtilsLog::message().CLAUDE.md updated, docs/translations.txt extended with the strings that were missing.Translations: the
.pofile ships new strings; the.mofile must be rebuilt (e.g. viawp i18n make-mo languages/or Poedit) before the new Czech wording is visible. This release does not regenerate the binary.
prod_var_type column was empty in prepare_to_printing_export.csv. Variation attribute is now resolved through postmeta.attribute_pa_format joined against wp_terms.name.custom_order_tables compatibility.For bug reports, feature requests or commercial support, contact quadarax.com.
Released under the GNU General Public License v2.0 or later — see https://www.gnu.org/licenses/gpl-2.0.html.