A production-ready WordPress plugin for exporting WooCommerce orders to CSV/XLSX with support for product-level custom codes and metadata.
- Flexible Export Formats: CSV (default) and XLSX output
- Custom Column Selection: Choose which order, item, and product fields to include
- Product Custom Codes: Map product meta keys or taxonomy terms to custom columns
- Export Modes: Export as one row per line item or one row per order
- Advanced Filtering: Filter orders by date range and status
- Performance Optimized: Stream output for large datasets (3,000+ orders)
- Export Logging: Track all export operations with filters applied
- Extensible: Hooks and filters for custom development
- Security: Proper nonces, capability checks, and sanitization
- WordPress 6.0+
- WooCommerce 6.0+
- PHP 7.4+
This plugin integrates with and supports the following plugins:
- Product Input Fields for WooCommerce
- Feature: Line Item Metadata Mapping
- Description: Export custom product input field metadata to your CSV/XLSX exports
- Status: Optional (feature is only available when this plugin is installed and activated)
- Use Case: Extract custom metadata collected via Product Input Fields and include it in order exports
- Download the plugin or clone into your
wp-content/plugins/directory - Navigate to the plugin in WordPress admin and activate
- Go to WooCommerce → Custom Order Export
-
Select Filters
- Choose date range (optional)
- Select order statuses to include
-
Choose Export Format
- CSV or XLSX
- Set CSV delimiter (comma, semicolon, tab, pipe)
- Configure column headers inclusion
-
Select Columns
- Order fields (ID, date, customer info, totals)
- Line item fields (product name, SKU, quantity, price)
- Product fields (categories)
-
Add Custom Code Mappings
- Map product meta keys or taxonomies to export columns
- Supports multiple terms per product
Export a product custom field stored as post meta:
Column Name: product_code
Source Type: Product Meta
Meta Key: _product_code
This exports the value of get_post_meta( $product_id, '_product_code', true )
Export a product taxonomy (like a custom "Collection" taxonomy):
Column Name: product_collection
Source Type: Taxonomy
Taxonomy Name: product_collection
This exports wp_get_post_terms( $product_id, 'product_collection', array('fields' => 'names') )
For WooCommerce attributes (stored as taxonomy pa_*):
Column Name: metal_type
Source Type: Taxonomy
Taxonomy Name: pa_metal
By default, the export includes:
order_id, order_date, customer_name, customer_email, billing_phone,
shipping_address, payment_method, order_total, product_id, sku,
product_name, quantity, line_total, product_code, product_categories
The plugin creates a logging table (wp_wexport_logs) with the following columns:
id: Auto-increment IDexport_date: Timestamp of exportfilters: JSON of filters appliedfile_path: Path to exported filerows_exported: Number of rows in exportexport_format: csv or xlsxuser_id: ID of user who triggered exportstatus: success or errorerror_message: Error details if applicable
// Add or modify order query arguments
apply_filters( 'wexport_order_query_args', $args );
// Modify custom code mappings
apply_filters( 'wexport_custom_code_mappings', $mappings );// Before export starts
do_action( 'wexport_before_export', $manager );
// After export completes
do_action( 'wexport_after_export', $manager, $file_path, $rows_exported );- In your product edit screen or via code, save product meta:
update_post_meta( $product_id, '_product_code', 'PROD-12345' );- In WExport, add a custom code mapping:
- Column Name:
product_code - Source Type:
Product Meta - Meta Key:
_product_code
- Column Name:
- Register a custom product taxonomy:
register_taxonomy(
'product_code',
'product',
array(
'label' => 'Product Code',
'rewrite' => array( 'slug' => 'product-code' ),
)
);- Assign terms to products via admin or code:
wp_set_post_terms( $product_id, array( 'PROD-12345' ), 'product_code' );- In WExport, add a custom code mapping:
- Column Name:
product_code - Source Type:
Taxonomy - Taxonomy Name:
product_code
- Column Name:
woocommerce-custom-order-export/
├── woocommerce-custom-order-export.php # Main plugin file
├── composer.json # Composer dependencies
├── composer.lock # Locked dependency versions
├── phpunit.xml # PHPUnit configuration
├── README.md # This file
├── CHANGELOG.md # Version history
├── LICENSE # GPL v2 license
├── .gitignore # Git ignore rules
│
├── includes/ # Core plugin classes
│ ├── class-export-manager.php # Main export orchestration
│ ├── class-export-formatter.php # CSV/XLSX data formatting
│ ├── class-export-logger.php # Export logging and tracking
│ ├── class-xlsx-exporter.php # XLSX file generation
│ ├── class-ajax-handler.php # AJAX request handling
│ ├── class-template-manager.php # Template CRUD operations
│ ├── class-template-ajax-handler.php # Template AJAX operations
│ └── class-import-manager.php # Template import/export
│
├── admin/ # WordPress admin interface
│ ├── class-admin-page.php # Admin page controller
│ ├── admin-page.php # Admin UI HTML template
│ ├── css/
│ │ ├── admin-styles.css # Main admin styling
│ │ ├── notifications.css # Notification system styling
│ │ └── template-styles.css # Template UI styling
│ └── js/
│ ├── admin-ui.js # Main admin interface logic
│ ├── notifications.js # Notification system
│ └── template-manager.js # Template frontend logic
│
├── tests/ # PHPUnit tests
│ ├── bootstrap.php # Test bootstrap configuration
│ └── test-export-manager.php # Export manager tests
│
└── vendor/ # Composer dependencies
├── phpoffice/phpspreadsheet/ # XLSX generation library
├── phpunit/phpunit/ # PHPUnit testing framework
├── wp-coding-standards/wpcs/ # WordPress coding standards
└── [other dependencies...]
Run PHPUnit tests using Composer:
# Run all tests
composer test
# Run with coverage report
composer test -- --coverage-html coverage/Tests are located in the tests/ directory and configured via phpunit.xml.
-
Verify Product Meta Export
- Create a product with custom meta key
_test_code= "TEST123" - Add custom code mapping in WExport (Meta type)
- Export and verify column contains "TEST123"
- Create a product with custom meta key
-
Verify Taxonomy Export
- Assign a product to a custom taxonomy term
- Add custom code mapping in WExport (Taxonomy type)
- Export and verify column contains term name
-
Large Dataset Test
- With 3,000+ orders, verify export completes without memory errors
- Check that file is created and downloaded correctly
-
Multi-Item Order Test
- Create order with 3 line items
- Export in "line_item" mode: should see 3 rows
- Export in "order" mode: should see 1 row with products joined
-
Special Characters Test
- Create product with quotes, commas, newlines in name
- Verify CSV escaping works correctly in exported file
-
Date Range Filtering Test
- Export with both from and to dates: verify complete range is applied
- Export with only from date: verify all orders after that date
- Export with only to date: verify all orders before that date
-
Template System Test
- Save a template with custom columns
- Load the template and verify all settings are restored
- Set as default and reload page to verify auto-loading
- Duplicate and edit templates
- Orders are fetched in batches (default 100 per batch)
- CSV is streamed to disk via
fputcsv() - No order or item data stored in memory simultaneously
- Processing time depends on server speed and data size
- Recommend increasing PHP timeout for very large exports
- Consider exporting by date ranges for large organizations
Ensure /wp-content/uploads/wexport/ directory exists and is writable:
chmod 755 wp-content/uploads/wexport/-
Verify the meta key exists on products:
get_post_meta( $product_id, '_your_meta_key', true )
-
Verify the taxonomy exists and is assigned to product:
wp_get_post_terms( $product_id, 'your_taxonomy' )
-
Check that custom code mapping is configured in WExport settings
Ensure "Include column headers" and "Use UTF-8 BOM" are both checked for Excel compatibility.
- includes/ - Core plugin classes (Export_Manager, Export_Logger, Exporters, Template_Manager)
- admin/ - WordPress admin interface (class-admin-page.php, templates, styles, JavaScript)
- tests/ - PHPUnit unit and integration tests
- vendor/ - Composer dependencies (PhpSpreadsheet, WordPress Coding Standards)
The project follows WordPress Coding Standards. Run checks with:
# Check code style
composer phpcs
# Auto-fix style issues
composer phpcbf-
Export_Manager (
includes/class-export-manager.php)- Orchestrates the export process
- Queries orders in batches
- Applies filters and custom code mappings
- Delegates formatting to exporter classes
-
CSV_Exporter / XLSX_Exporter (
includes/class-*-exporter.php)- Format-specific export logic
- Stream data to files
- Handle styling and encoding
-
Export_Logger (
includes/class-export-logger.php)- Tracks export operations in database
- Logs filters applied, file paths, status
-
Template_Manager (
includes/class-template-manager.php)- User-scoped template persistence
- CRUD operations for templates
- Default template handling
-
Admin_Page (
admin/class-admin-page.php)- Generates admin UI
- Handles form data validation
- Provides preview functionality
- Update column selection UI in
admin/admin-page.php - Add column to available columns list in
Export_Manager::get_available_columns() - Implement column data retrieval in
Export_Formatter::format_item() - Update default export columns if needed
- Create filter UI in
admin/admin-page.php - Modify
Export_Manager::get_orders_batch()to apply new filter - Update
Export_Loggerto track new filter type - Add validation in
Admin_Page::validate_filters()
- Create new exporter class extending base logic
- Implement file writing in
export()method - Register in
Export_Manager::get_exporter() - Add format option to admin UI
Filters:
// Modify order query arguments
apply_filters( 'wexport_order_query_args', $args );
// Modify custom code mappings
apply_filters( 'wexport_custom_code_mappings', $mappings );
// Modify export columns
apply_filters( 'wexport_export_columns', $columns );Actions:
// Before export starts
do_action( 'wexport_before_export', $manager );
// After export completes
do_action( 'wexport_after_export', $manager, $file_path, $rows_exported );Enable debug logging by adding to wp-config.php:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );Check logs in wp-content/debug.log. The plugin uses error_log() for server-side debugging.
For frontend debugging, check browser console (F12) for detailed logging during export operations.
# Install dependencies
composer install
# Run tests
composer test
# Run with verbose output
composer test -- --verboseTest files use WordPress test utilities if bootstrapped properly. See tests/bootstrap.php for configuration.
Extend export columns via filter:
add_filter( 'wexport_order_query_args', function( $args ) {
$args['meta_query'] = array(
array(
'key' => '_custom_order_field',
'value' => 'value',
)
);
return $args;
});Use WP-Cron:
wp_schedule_event( time(), 'daily', 'wexport_scheduled_export' );
add_action( 'wexport_scheduled_export', function() {
$manager = new WExport\Export_Manager( array(
'format' => 'csv',
'columns' => get_option( 'wexport_default_columns' ),
'order_status' => array( 'wc-completed' ),
));
$file = $manager->export();
// Email result
wp_mail(
get_option( 'admin_email' ),
'Daily Order Export',
'Export completed: ' . $file
);
});Typical performance on standard hosting:
- 100 orders: < 1 second
- 1,000 orders: 5-10 seconds
- 10,000 orders: 1-2 minutes
- 100,000 orders: 15-30 minutes
- All admin actions require
manage_woocommercecapability - Nonces on all forms
- User ID and timestamp logged for all exports
- Meta keys and taxonomy names sanitized before database queries
- CSV output escaped for safe Excel import
For issues or feature requests, please contact support or submit via the plugin support channel.
This plugin is developed with contributions from the community:
Special thanks to:
- @BrianHenryIE - Line Item Metadata Mapping feature
- Fixed: XLSX exports now properly populate all row data (previously returned empty rows)
- Fixed: Removed unnecessary row data type conversion that broke column-to-value mapping
- Improved: XLSX row data structure maintains associative array format for proper exporter compatibility
- Fixed: Preview/export mismatch where preview displayed data but export returned empty file
- Fixed: Date filter inconsistency between preview and export functions
- Fixed: Unreachable code in AJAX handler preventing option persistence
- Changed: Preview now applies same date filters as export for consistent results
- Added: Line Item Metadata Mapping feature to export custom metadata from Product Input Fields products
- Added: Support for nested JSON query paths in metadata extraction
- Added: Conditional UI that shows/hides metadata feature based on plugin installation
- Added: Unit tests for metadata configuration and export functionality
- Fixed: Enhanced AJAX and form handlers with plugin presence verification
- Contributors: @BrianHenryIE for initial metadata feature implementation
- Fix: Re-registered autoloader via
spl_autoload_registerwhich was lost afterWExportclass refactor. - Fix: Corrected autoloader directory mapping for
WExport\Adminnamespace to fix admin area disappearance.
- Changed: Decoupled bootstrap initialization by moving
WExportclass toincludes/class-wexport.php. - Fixed: Multiple WordPress Coding Standards issues.
- Improved: Hardened security with better nonce validation and input sanitization.
- Optimized: Preview generation for small datasets is now faster.
Changed
- Refactoring: Moved
WExportcore class to includes/class-wexport.php to separate bootstrap logic from core architecture and improve PSR autoloading.
Fixed
- WP Coding Standards: Resolved numerous violations across the codebase, including inline comment punctuation, docblock @throws tags, for better compliance with WordPress standards.
- Security: Strengthened security by adding missing nonce verification and proper
$_POSTsanitization in the admin module. - File System: Improved directory handling by integrating
WP_Filesystemfor path and permission checks. - Preview Optimization: Refactored the preview generation logic to avoid redundant file system writes/reads, keeping processing in-memory where possible.
Fixed
- Critical: Taxonomy custom columns now correctly show only the variation-specific attribute value for variation products, instead of showing all taxonomy terms from the parent product. When exporting a variation with a selected attribute (e.g., "50g" for
pa_gramaj), only that specific value is now exported, not all available values. - Product variation taxonomy extraction now prioritizes
get_attribute()to retrieve the variation-specific value before falling back towp_get_post_terms(). - Preview and export now have consistent behavior for variation taxonomy values.
- Persist
remove_variation_from_product_nameadmin setting: Store the checkbox state persistently usingupdate_optionfrom both AJAX and non-AJAX flows (preview and export), so the checkbox is remembered for subsequent sessions.
Changed
- Taxonomy handling: Reordered variation attribute extraction logic to check
product->get_attribute()FIRST for variations, ensuring only the selected attribute value is returned, then falling back to taxonomy term queries only if needed. - Simplified taxonomy extraction logic by removing redundant attribute fallback from the else block.
Added
- Checkbox in admin UI to remove variation details from product names on export and preview. The exporter now uses parent product name for variations if this option is enabled.
Changed
- Taxonomy-based custom code mappings now prefer terms assigned to the variation (if the exported item is a variation) and fall back to the parent product if none are present. This makes per-variation attributes (e.g.,
pa_gramaj) export the correct variation value (like "50g").
Fixed
- Date Range Filtering: Fixed critical bug where date range filters were not working correctly
- Previously, when both
date_fromanddate_towere specified, the second filter would overwrite the first - Now uses proper WooCommerce query approach with post-filtering for combined date ranges
- Single date filters (from OR to) use native WooCommerce date_created parameter
- Combined date range filters use PHP-level post-filtering to avoid WooCommerce HPOS incompatibility
- Orders are now correctly filtered by the complete date range
- Previously, when both
- Export now respects both start and end dates when filtering orders
- Fixed TypeError from incompatible date query format with WooCommerce HPOS
Changed
- Improved
get_orders_batch()method with hybrid approach: native query for single conditions, post-filtering for ranges - Date filtering now uses time boundaries (00:00:00 for start, 23:59:59 for end) for accurate daily filtering
- Added custom parameter handling (_wexport_date_from, _wexport_date_to) for post-filtering logic
Changed
- UX Improvement: Replaced browser alerts with user-friendly flyout notifications
- Alerts in template loading logic now display as non-intrusive notification popups
- Notifications support multiple types: success, error, warning, and info
- Auto-dismiss notifications after 3-5 seconds with manual close option
- Improved visual feedback with color-coded notification types
Added
- New notification system with dedicated CSS styling (
admin/css/notifications.css) - Notification JavaScript utility (
admin/js/notifications.js) withWExportNotificationsAPI - Smooth animations for notification appearance and dismissal
- Responsive design for mobile and tablet devices
- Notification types: success (green), error (red), warning (orange), info (blue)
- Template Management Improvements:
- Display currently loaded template in Template section with visual indicator
- "Edit" button for templates to rename and update their configurations
- Visual highlighting of currently loaded template in manage modal
- Track currently loaded template state across sessions
Fixed
- Simplified: Removed unnecessary
plugin_versionandoperation_typecolumns from logging table - Cleaned up database schema to only store essential export information
- Removed complex migration logic that was causing database issues
Changed
- Export logger now only tracks: filters, file_path, rows_exported, export_format, user_id, status, error_message
- Removed
log_export()parameters:operation_type,plugin_version - Removed
migrate_table()method and all related migration logic - Simplified table schema to reduce complexity
Fixed
- Critical: Database migration now properly adds missing
plugin_versionandoperation_typecolumns on export - Simplified migration logic to use direct ALTER TABLE statements instead of dbDelta
- Migration now runs automatically before every export to ensure columns exist
- Added error logging for migration failures
Changed
- Migration function now checks column existence with
INFORMATION_SCHEMAbefore attempting to add - Export AJAX handler now calls
migrate_table()before processing exports - Transient-based migration caching on page load to avoid performance issues
Added
- Auto-load default template - When a user sets a template as default, it now automatically loads on page visit
- Default template ID is passed to frontend and loaded on admin page initialization
Changed
- Template initialization now checks for and loads the default template if one is set
- Updated frontend data localization to include
defaultTemplateId loadDefaultTemplateIfSet()method added to template manager for automatic loading
Fixed
- Critical: Database table migration not running on existing installations
- Added automatic database migration on every plugin load to ensure missing columns are created
- Fixed
plugin_versionandoperation_typecolumns missing on tables created with older versions
Changed
- Plugin now runs
migrate_table()onplugins_loadedhook in addition to activation - Ensures backward compatibility with all existing installations without manual SQL intervention
Fixed
- Critical: Custom code mappings source field was always empty when saving templates
- Fixed form data collection to properly read from the correct input element (text vs select)
- For taxonomy types, now correctly reads from
.custom-code-source-selectinstead of generic.custom-code-source - For meta types, correctly reads from
.custom-code-source-text
Fixed
- Fixed custom code mappings not being properly serialized when saving templates
- Improved template save AJAX request to use FormData API for proper nested array handling
- Added comprehensive debug logging to track data flow during template save operations
Changed
- Template save now uses FormData API instead of plain object spread
- Enhanced error reporting in browser console and server logs
- Added detailed logging for custom codes array processing
Fixed
- Critical: Template system now correctly loads custom column mappings when loading saved templates
- Custom code mappings were not being restored when loading templates due to form data mismatch
- Form now properly clears existing custom code rows before loading template rows
- Added null/undefined checks for safely handling template data
Added
- Template Management System - Save, load, duplicate, and manage export templates
- New
Template_Managerclass for handling template CRUD operations - New
Template_Ajax_Handlerclass for AJAX template operations - Template dropdown selector for quick template loading
- "Manage Templates" modal with table view of all saved templates
- Set default template functionality for automatic loading on page load
- Template duplication and deletion capabilities
- Template import/export as JSON for backup and sharing
Changed
- Updated admin page to include template management interface
- Added template JavaScript handler for frontend operations
- Plugin version bumped from 1.3.8 to 1.4.0
Fixed
- Critical: XLSX export styling methods now compatible with PhpSpreadsheet 1.29+
- Fixed
getFont()method - now usesgetStyle()->getFont() - Fixed
getFill()method - now usesgetStyle()->getFill() - Fixed
getDataType()method - now usesgetStyle()->getNumberFormat() - XLSX header styling (bold, gray background) now works correctly
- Number formatting for decimal values in XLSX files now works
Fixed
- Important: Product variations now correctly export parent product taxonomy terms
- Variation products now inherit category and taxonomy assignments from their parent product
- Product categories now properly resolve for all product variation types
Added
- Automatic parent product detection for variation products
- Proper handling of WooCommerce product variation inheritance in taxonomy extraction
Fixed
- Critical: Taxonomy form data not being properly captured during AJAX form submission
- Fixed FormData construction to correctly extract values from taxonomy select dropdowns
- Improved input visibility toggle to use CSS display properties instead of jQuery show/hide
- Taxonomy terms now properly export when selected from the custom code mapping dropdown
Fixed
- Critical: Custom taxonomy term mappings showing no values in export
- Improved form validation to properly capture taxonomy select values
- Fixed visibility toggle detection in form data collection
- Added taxonomy validation to prevent silent failures on invalid taxonomy names
Fixed
- Critical: Product taxonomy term data not being exported to CSV despite appearing in preview
- Custom taxonomy term extraction now retrieves full term objects instead of only term names
- Both export and preview now access complete term data with proper separators
Fixed
- Critical: Custom code mappings not being included in export/preview
- Form data submission now uses FormData API instead of jQuery serialize()
- Properly handles nested array structure for custom codes (
custom_codes[index][field]) - Custom code columns and product data now correctly appear in exports
Added
- Debug logging for custom code mapping issues
- Browser console logging to show custom codes before AJAX submission
- Server-side error logging for custom code processing (wp-content/debug.log)
Fixed
- Bug: Validation error when using taxonomy dropdown selector
- Form validation now checks only the visible input field (text for meta, dropdown for taxonomy)
- Fixed "Please fill in both Column Name and Source" error during preview/export
Added
- New Feature: Dynamic taxonomy dropdown selector for custom product code mappings
get_available_product_taxonomies()method to fetch all public product taxonomies- Smart input type switching in custom codes UI - text input for meta, select dropdown for taxonomies
- Taxonomy labels displayed with technical names for better user clarity (e.g., "Color (pa_color)")
Fixed
- Bug: Product custom metas not included in preview generation
- Custom code mappings now correctly show in preview data
- Preview now matches final export output
Features
- CSV and XLSX export support
- Product custom codes mapping (meta and taxonomy)
- Order filtering by date range and status
- Export logging and recent exports display
- Flexible column selection
- Template system foundation
Initial Release
- Basic CSV and XLSX export functionality
- WooCommerce order export with customizable columns
- Support for product metadata and custom fields
- Date range and order status filtering
- Export logging to track operations
- Full WordPress and WooCommerce integration
- Security features: nonce verification, capability checks, input sanitization
This plugin is licensed under the GPL v2 License.
Made with 💙 by the WooCommerce Custom Order Export community