Skip to content

Redesign email settings with granular per-email toggles and cashier notifications#502

Merged
kilbot merged 4 commits intomainfrom
feature/email-settings-redesign
Feb 9, 2026
Merged

Redesign email settings with granular per-email toggles and cashier notifications#502
kilbot merged 4 commits intomainfrom
feature/email-settings-redesign

Conversation

@kilbot
Copy link
Copy Markdown
Contributor

@kilbot kilbot commented Feb 9, 2026

Summary

Closes #430

  • Redesigns email settings from simple on/off booleans to granular per-email-type toggles across three categories: admin, customer, and cashier emails
  • Adds cashier email support so the POS cashier can receive new_order notifications for orders they process
  • Uses WooCommerce's woocommerce_email_recipient_new_order filter (not the enabled filter) so admin and cashier new_order toggles work independently
  • Migrates existing boolean settings to the new array format transparently on read

Settings structure

  • Admin emails: master toggle + individual toggles for new_order, cancelled_order, failed_order
  • Customer emails: master toggle + individual toggles for customer_on_hold_order, customer_processing_order, customer_completed_order, customer_refunded_order, customer_failed_order
  • Cashier emails: master toggle + individual toggle for new_order (cashier identified from _pos_user order meta)

Test plan

  • 29 email-specific tests covering:
    • Hook registration for all email types
    • Admin enabled filter (master on/off, individual toggles, non-POS passthrough)
    • Customer enabled filter (master on/off, individual toggles, non-POS passthrough)
    • New order recipient filter (10 cases: admin+cashier, admin-only, cashier-only, both off, dedup when cashier is admin, no cashier meta, empty cashier email, non-POS passthrough)
    • Trigger gate logic for pos-open/pos-partial transitions
    • Legacy boolean-to-array migration
    • Custom filter hooks (woocommerce_pos_admin_email_enabled, woocommerce_pos_customer_email_enabled)
  • All 677 tests passing, 0 failures
  • PHPCS lint clean on all modified files

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Granular per-event email settings for admin, customer, and new cashier notifications.
    • New cashier email controls and recipient filtering for POS new-order notifications.
    • Legacy boolean email settings migrate to the new array-based structure; notification triggering now respects admin/cashier preferences and deduplicates recipients.
  • Tests

    • Expanded coverage for checkout email settings, cashier behavior, legacy migration, recipient resolution, and POS order flows.

…hier notifications

Replaces flat boolean admin_emails/customer_emails settings with nested
arrays supporting master toggles and individual per-email-type control.
Adds cashier_emails category so POS cashiers can receive new_order
notifications independently of admin settings.

Key changes:
- Settings structure migrated from bool to array with backwards compat
- new_order recipients controlled via recipient filter (not enabled filter)
  so admin and cashier toggles work independently
- Cashier identified from _pos_user order meta
- Dedup prevents duplicate emails when cashier is also admin

Closes #430
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 9, 2026

📝 Walkthrough

Walkthrough

Checkout email settings changed from booleans to structured arrays (admin, customer, cashier); API validation updated to accept arrays; emails handler adds cashier recipient filtering and per-email enablement; settings service migrates legacy boolean values; tests expanded for cashier behavior and migration.

Changes

Cohort / File(s) Summary
API / Settings
includes/API/Settings.php
Validation for checkout endpoint args: admin_emails/customer_emails now expect array shapes; cashier_emails added to schema.
Settings Service
includes/Services/Settings.php
Checkout settings restructured to nested arrays with enabled + per-event flags; cashier_emails added; migration logic converts legacy boolean flags to new array form.
Email handling
includes/Emails.php
Adds filter_new_order_recipients() and get_cashier_email(); introduces per-email enablement filters for admin/customer; triggers and recipient resolution updated to include cashier logic and deduplication.
Tests — Emails
tests/includes/Test_Emails.php
Major test refactor: set_checkout_settings(array,array,array) replaces boolean setter; create_pos_order(..., $cashier_id) added; create_email_mock() added; extensive tests for cashier recipients, per-email toggles, migration, and deduplication.
Tests — Settings API & Service
tests/includes/API/Test_Settings_API.php, tests/includes/Services/Test_Settings_Service.php
Tests updated to expect array-shaped checkout email settings, mock REST request updated for JSON params, and new cases added for cashier_emails and richer payloads.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant REST as REST API\n(includes/API/Settings.php)
    participant Settings as Settings Service\n(includes/Services/Settings.php)
    participant Order as POS Order
    participant Emails as Emails Handler\n(includes/Emails.php)
    participant Mailer as WooCommerce Mailer

    Client->>REST: GET/PUT checkout settings (includes `cashier_emails`)
    REST->>Settings: validate & migrate settings
    Settings-->>REST: return structured checkout settings
    Client->>Order: Create POS order (includes _pos_user / cashier id)
    Order->>Emails: Trigger new_order flow
    Emails->>Settings: read admin/customer/cashier toggles
    Emails->>Emails: get_cashier_email(order)
    Emails->>Mailer: assemble recipient list (admin + cashier if enabled, dedupe)
    Mailer-->>Emails: send(new_order)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through toggles, arrays snug and bright,
Cashier and admin join the email flight,
Filters gather, dedupe, and then send on cue,
Tests thump their paws to make sure it’s true,
A little rabbit cheers — new orders reach you!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main changes: granular per-email toggles replacing boolean settings and cashier notification support.
Linked Issues check ✅ Passed All requirements from issue #430 are met: cashier_emails setting added, woocommerce_email_recipient_new_order filter implemented, REST API exposed, and comprehensive test coverage included.
Out of Scope Changes check ✅ Passed All changes directly support the core objectives; no out-of-scope modifications detected. Email settings redesign, cashier support, and test updates are all aligned with issue requirements.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/email-settings-redesign

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
tests/includes/Test_Emails.php (1)

707-789: Assert that a new_order email was captured in the “on” cases.

assertTrue(true) doesn’t validate behavior; these tests could pass even if the trigger no‑ops. Consider asserting that EmailHelper captured a new_order send.

Suggested stronger assertions
@@
 	$emails->trigger_new_order_email( $order->get_id(), $order );
 
-	$this->assertTrue( true, 'Email trigger should run without error' );
+	$new_order_emails = EmailHelper::get_emails_by_wc_id( 'new_order' );
+	$this->assertNotEmpty( $new_order_emails, 'new_order email should be sent when admin and cashier are enabled' );
@@
 	$emails->trigger_new_order_email( $order->get_id(), $order );
 
-	$this->assertTrue( true, 'Email trigger should run for cashier even when admin is off' );
+	$new_order_emails = EmailHelper::get_emails_by_wc_id( 'new_order' );
+	$this->assertNotEmpty( $new_order_emails, 'new_order email should be sent when cashier is enabled' );
@@
 	$emails->trigger_new_order_email( $order->get_id(), $order );
 
-	$this->assertTrue( true, 'Email trigger should run for admin' );
+	$new_order_emails = EmailHelper::get_emails_by_wc_id( 'new_order' );
+	$this->assertNotEmpty( $new_order_emails, 'new_order email should be sent when admin is enabled' );

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 9, 2026

E2E API Test Results

35 tests   35 ✅  3s ⏱️
17 suites   0 💤
 1 files     0 ❌

Results for commit febac8e.

♻️ This comment has been updated with latest results.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Feb 9, 2026

PHPUnit Test Results

696 tests  +17   688 ✅ +18   56s ⏱️ +4s
 39 suites ± 0     8 💤  -  1 
  1 files   ± 0     0 ❌ ± 0 

Results for commit febac8e. ± Comparison against base commit 29e6290.

This pull request removes 14 and adds 31 tests. Note that renamed tests count towards both.
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_constructor_registers_actions
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_constructor_registers_filters
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_manage_admin_emails_with_order_id
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_manage_admin_emails_with_wc_email
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_manage_customer_emails_various_ids
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_trigger_new_order_email_enabled
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_direct_trigger_new_order_email_loads_order
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_manage_admin_emails_disabled_for_pos_order
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_manage_admin_emails_enabled_for_pos_order
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_manage_admin_emails_unaffected_for_non_pos_order
…
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_admin_email_disabled_master_off
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_admin_email_enabled_master_on_individual_off
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_admin_email_enabled_master_on_individual_on
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_admin_email_individual_defaults_true
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_admin_emails_unaffected_for_non_pos_order
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_cashier_emails_defaults_on_fresh_install
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_customer_email_disabled_master_off
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_customer_email_enabled_master_on_individual_off
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_customer_email_enabled_master_on_individual_on
WCPOS.WooCommercePOS.Tests.Test_Emails ‑ test_customer_emails_unaffected_for_non_pos_order
…

♻️ This comment has been updated with latest results.

…rmat

- Replace stdClass mocks with WC_Email mocks in Test_Emails so
  instanceof checks pass and individual toggle tests work correctly
- Update Test_Settings_API and Test_Settings_Service assertions to
  expect the new array format for email settings instead of booleans
- Fix all PHPCS lint errors in both settings test files
- Fix test_new_order_recipients_dedup_cashier_is_admin: use existing
  admin user instead of creating a new one (WP rejects duplicate emails)
- Fix test_new_order_recipients_cashier_no_email: create user with valid
  email then wipe via DB (WP rejects empty emails during user creation)
- Fix test_email_id_extraction_from_wc_email: instantiate
  WC_Email_Customer_Processing_Order directly instead of relying on the
  mailer which is reset in setUp
@kilbot kilbot merged commit f862f4b into main Feb 9, 2026
17 checks passed
@kilbot kilbot deleted the feature/email-settings-redesign branch February 9, 2026 16:37
kilbot added a commit that referenced this pull request Feb 11, 2026
PR #502 added per-email-type controls and cashier emails to the backend,
but the frontend still rendered simple boolean toggles. This updates the
checkout settings page to surface the full email settings structure with
master toggles and individual email type checkboxes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Send new order email to cashier

1 participant