Export WooCommerce orders for your accountant without sharing customer data: a plugin you can build today
· 4 min read
Most WooCommerce stores reach a point where an accountant needs to see the sales data. The default path is WooCommerce > Orders, select all, export to CSV. It works. The file lands with your accountant containing the order ID, the date, the total, and the products. It also contains every customer's name, email address, billing address, and phone number.
An accountant reconciling invoices against bank statements does not need any of that. Under data minimisation principles in GDPR and similar laws elsewhere, sharing more personal data than the task requires is not just unnecessary, it is something you are expected to avoid. The practical risk varies by jurisdiction, but the habit of exporting everything and emailing the whole file to a third party is exactly what those regulations are designed to stop.
A plugin that produces an accountant-safe export is small. It needs one admin screen with a date range, one button, and a CSV generator that includes the financial columns and leaves the personal ones behind. That is the plugin this guide describes.
What one build gives you
- An Accountant Export page under WooCommerce with optional From and To date fields
- One CSV row per order: ID, date, status, payment method, line items, shipping, tax, total, coupons
- All customer personal data stripped: no name, email, address, phone, or user ID
- Safe to send to an external accountant without reviewing each column for personal information
What it does not do
- Direct integration with accounting software such as Xero, QuickBooks, or FreshBooks
- Pivot or summary views: no totals by product, category, or VAT rate
- Country-level tax breakdowns for multi-jurisdiction filings
- Filtering by order status: all statuses are included by default
- Scheduled or automatic delivery of the export by email
The prompt
Loads into the composer so you can edit it first. Nothing is built, and nothing is charged, until you send it.
Build a WordPress plugin called WooCommerce Accountant Export. Plugin Name: WooCommerce Accountant Export. Description: Exports WooCommerce order data as a CSV with all customer personal information removed. Requires WooCommerce to be active. Single PHP file. No dependencies beyond WordPress core and WooCommerce. Check that WooCommerce is active at plugins_loaded using is_plugin_active. If WooCommerce is not active, add an admin_notices callback that displays a warning and return early from the rest of the setup. Register an admin submenu page using add_submenu_page: parent_slug woocommerce, page_title Accountant Export, menu_title Accountant Export, capability manage_woocommerce, menu_slug wc-accountant-export. Hook this to admin_menu. The admin page callback renders an H1 heading Accountant Export and a form with method POST and action pointing to the current page URL (use admin_url with the menu slug). The form contains: a nonce field with action wc_accountant_export, a label Order from with a date input named export_from (current value from $_POST if set, escaped with esc_attr), a label Order to with a date input named export_to (current value from $_POST if set, escaped with esc_attr), and a submit button with name do_export and value 1 labelled Download CSV. At the top of the admin page callback, before rendering the form, check whether do_export is set in $_POST. If it is: verify the nonce with check_admin_referer('wc_accountant_export'). Read export_from and export_to from $_POST using sanitize_text_field. Build $args for wc_get_orders: limit -1, orderby date, order DESC, status as an array containing wc-pending, wc-processing, wc-on-hold, wc-completed, wc-cancelled, wc-refunded, wc-failed. If export_from is not empty, set date_after in args to export_from. If export_to is not empty, set date_before in args to export_to. Set HTTP headers before any output: Content-Type text/csv; charset=utf-8, Content-Disposition attachment; filename=woocommerce-accountant-export- followed by date('Y-m-d') followed by .csv, Pragma no-cache, Cache-Control must-revalidate, Expires 0. Open a write handle with fopen('php://output', 'w'). Write a header row using fputcsv with these columns in order: Order ID, Date, Status, Payment Method, Items, Shipping, Tax, Total, Coupons. Loop over the orders returned by wc_get_orders($args). For each $order: build an $items_string by iterating $order->get_items() and for each item building the string get_name() . ' x' . get_quantity() . ' @ ' . number_format(($item->get_total() / max($item->get_quantity(), 1)), 2). Join multiple items with '; '. Write one row using fputcsv with: $order->get_id(), $order->get_date_created()->date('Y-m-d H:i:s'), $order->get_status(), $order->get_payment_method_title(), $items_string, number_format($order->get_shipping_total(), 2), number_format($order->get_total_tax(), 2), number_format($order->get_total(), 2), implode(', ', $order->get_coupon_codes()). Do not read or output: customer name, customer email, billing address fields, shipping address fields, customer phone, customer IP address, customer user ID, or any other personally identifying field. After the fputcsv loop, call fclose on the handle and then call exit to prevent WordPress from appending any HTML.
What the plugin does
After installation, an Accountant Export entry appears in the WooCommerce menu. The screen has two optional date fields, a From date and a To date, and a Download CSV button. Leave both fields blank to export every order in the store. Fill them in to narrow the range to a quarter, a month, or any period you need to file.
The download starts immediately. The file opens directly in a spreadsheet application, one row per order. There is no confirmation step and no intermediate screen.
- One page under WooCommerce with a date range filter and a Download CSV button
- One CSV row per order, covering the full order history by default
- Dates filter independently: set only a From date to export everything from that point forward
- No settings page and no configuration required after installation
What the CSV contains
The file has nine columns: Order ID, Date, Status, Payment Method, Items, Shipping, Tax, Total, Coupons. Every column contains either a reference number, a date, a financial figure, or a method of payment.
The Items column holds a summary of the line items in that order: product name, quantity, and unit price for each item, joined by a semicolon if there are several. It is readable in a single cell rather than split across multiple rows, which keeps the file easy to scroll and filter.
Customer name, email address, billing address, shipping address, phone number, and IP address are absent. There is no WordPress user ID either. Nothing in the file links an order to a specific individual.
- Order ID, date, and status in each row
- Payment method as shown at checkout, such as Credit Card or BACS
- Line items: product name, quantity, and unit price per item in one cell
- Shipping total, tax total, and order total as decimal numbers
- Coupon codes applied to the order, comma-separated
What this plugin does not do
The file is a flat export. It does not connect to Xero, QuickBooks, FreshBooks, or any other accounting platform. If your accountant imports data directly into accounting software, check whether that platform has a dedicated WooCommerce integration. Those connectors typically operate under a data processing agreement that handles the privacy question on different terms.
The export shows one row per order. It does not pivot by product, group by category, calculate gross margin, or break out VAT by country or region. For a summary of sales by period or a breakdown by product line, you will need a reporting plugin or a few minutes with the spreadsheet after export.
It also does not filter by order status. The default includes all statuses: pending, processing, on-hold, completed, cancelled, refunded, and failed. If your accountant only wants completed orders, or wants refunds as separate rows, both are short follow-up builds.
Extending the export
Because you own the code, additions are short follow-up requests. If you need to filter by status, ask Steem to add a checklist to the export screen so you can include or exclude pending, cancelled, and refunded rows. If your accountant wants the VAT rate per line item rather than a single tax total, ask for a column that reads each item's tax class. If you want the file emailed on the first of each month, ask for a scheduled wp_cron job that sends it to an address you specify.
A second build worth considering once the export is working: a read-only accountant login. A custom capability can grant access to the export screen and nothing else in the WordPress admin. Your accountant logs in, downloads their own file, and neither of you needs to exchange spreadsheets by email.
Questions
- Does this plugin make my store GDPR compliant?
No plugin makes your store compliant on its own. What this plugin does is remove unnecessary personal data from a specific export, reducing what you share with a third party. That supports data minimisation, which is one requirement among many. Your broader obligations depend on your jurisdiction and how your store is set up.
- Can I filter the export to show only completed orders?
Not by default. The export includes all order statuses. If you only want completed orders, or want to exclude failed and cancelled ones, ask Steem to add a status checklist to the export screen.
- My accountant imports directly into Xero. Will this file work?
No. This plugin produces a flat CSV for manual review and reconciliation, not a file formatted for import into accounting software. For direct Xero or QuickBooks integration, look at the dedicated WooCommerce connectors those platforms publish.
- Can I add more columns, like product SKU or shipping method?
Yes. Ask Steem to add any column available in the WooCommerce order object. Name the column and describe what should fill it: for example, the shipping method label, the product SKU for each line item, or a note field.
- Can the export be emailed to my accountant automatically each month?
Not by default, but it is a short addition. Ask Steem to add a wp_cron job that runs on the first of each month, generates the previous month's export, and sends it to an email address you specify in a settings field.
The prompt
Loads into the composer so you can edit it first. Nothing is built, and nothing is charged, until you send it.
Build a WordPress plugin called WooCommerce Accountant Export. Plugin Name: WooCommerce Accountant Export. Description: Exports WooCommerce order data as a CSV with all customer personal information removed. Requires WooCommerce to be active. Single PHP file. No dependencies beyond WordPress core and WooCommerce. Check that WooCommerce is active at plugins_loaded using is_plugin_active. If WooCommerce is not active, add an admin_notices callback that displays a warning and return early from the rest of the setup. Register an admin submenu page using add_submenu_page: parent_slug woocommerce, page_title Accountant Export, menu_title Accountant Export, capability manage_woocommerce, menu_slug wc-accountant-export. Hook this to admin_menu. The admin page callback renders an H1 heading Accountant Export and a form with method POST and action pointing to the current page URL (use admin_url with the menu slug). The form contains: a nonce field with action wc_accountant_export, a label Order from with a date input named export_from (current value from $_POST if set, escaped with esc_attr), a label Order to with a date input named export_to (current value from $_POST if set, escaped with esc_attr), and a submit button with name do_export and value 1 labelled Download CSV. At the top of the admin page callback, before rendering the form, check whether do_export is set in $_POST. If it is: verify the nonce with check_admin_referer('wc_accountant_export'). Read export_from and export_to from $_POST using sanitize_text_field. Build $args for wc_get_orders: limit -1, orderby date, order DESC, status as an array containing wc-pending, wc-processing, wc-on-hold, wc-completed, wc-cancelled, wc-refunded, wc-failed. If export_from is not empty, set date_after in args to export_from. If export_to is not empty, set date_before in args to export_to. Set HTTP headers before any output: Content-Type text/csv; charset=utf-8, Content-Disposition attachment; filename=woocommerce-accountant-export- followed by date('Y-m-d') followed by .csv, Pragma no-cache, Cache-Control must-revalidate, Expires 0. Open a write handle with fopen('php://output', 'w'). Write a header row using fputcsv with these columns in order: Order ID, Date, Status, Payment Method, Items, Shipping, Tax, Total, Coupons. Loop over the orders returned by wc_get_orders($args). For each $order: build an $items_string by iterating $order->get_items() and for each item building the string get_name() . ' x' . get_quantity() . ' @ ' . number_format(($item->get_total() / max($item->get_quantity(), 1)), 2). Join multiple items with '; '. Write one row using fputcsv with: $order->get_id(), $order->get_date_created()->date('Y-m-d H:i:s'), $order->get_status(), $order->get_payment_method_title(), $items_string, number_format($order->get_shipping_total(), 2), number_format($order->get_total_tax(), 2), number_format($order->get_total(), 2), implode(', ', $order->get_coupon_codes()). Do not read or output: customer name, customer email, billing address fields, shipping address fields, customer phone, customer IP address, customer user ID, or any other personally identifying field. After the fputcsv loop, call fclose on the handle and then call exit to prevent WordPress from appending any HTML.
Steem