Skip to content

feat: customer segments and campaigns - #105

Merged
roncodes merged 4 commits into
release/v0.4.22from
feat/promotion-campaigns
Sep 28, 2026
Merged

roncodes merged 4 commits into
release/v0.4.22from
feat/promotion-campaigns

Conversation

@roncodes

@roncodes roncodes commented Sep 26, 2026 •

Copy link
Copy Markdown
Member

Summary

Adds customer segments and campaigns, so stores and networks can send targeted notifications to their customers (push and/or the in-app inbox) now or on a schedule, and announce promotions automatically.

Stacked PR, part of release #107 (release/v0.4.22). It builds on #102 (push layer), #103 (inbox and preferences) and #104 (promotions), which this branch merges. Review only the last commit, db015df "feat: add customer segments and campaigns". The diff shrinks to it once those PRs merge into the release branch.

Segments (customer_segments)

Rule-based audiences of a store's or network's customers. All rules are optional and combined with AND. Order rules count the customer's non-canceled storefront orders placed with the owning store or network.

Rule Meaning
customers only these customers (contact uuids)
min_orders / max_orders order count bounds (max_orders: 0 means never ordered here)
ordered_within_days ordered in the last N days
not_ordered_within_days ordered before, but not in the last N days (lapsed)
joined_within_days new customers
min_spent total successful storefront payments (minor units, company wide)
has_push_device has an active push device

Campaigns (campaigns)

  • Message: title, body, optional image and deep link action (promotion, product, store, category or url).
  • Channels: push and/or inbox.
  • Audience: a segment, explicit recipients, both (intersected), or all customers.
  • Lifecycle: draft → scheduled (send_at) → sending → sent, or canceled.
  • Sending: storefront:dispatch-campaigns runs every minute. It claims due campaigns atomically (never sent twice), resolves the audience, and queues SendCampaignBatch jobs of 250 customers. stats records targeted and batches.
  • Preferences: customers who turned promotions off (feat: customer notifications inbox API #103) receive nothing.
  • Promotion checks: a campaign for a promotion that has been paused, ended or deleted by send time is canceled instead of sent.

Promotions trigger notifications

POST int/v1/promotions/{id}/announce (optional title, body, segment, channels, send_at) creates a campaign that deep links to the promotion and uses its image. It goes out when the promotion starts, or immediately if the promotion has already started.

Console API (storefront/int/v1)

customer-segments CRUD (validated, owner must be the company's store or network), GET {id}/preview and POST preview (owner_uuid, rules) return { count, sample[10] } for a live segment builder
campaigns CRUD (status limited to draft/scheduled), POST {id}/send (send now), POST {id}/cancel, GET {id}/audience ({ count })
promotions/{id}/announce see above

Permissions: new customer-segment (preview) and campaign (send, cancel) resources, plus announce on promotion. The PromotionsManager policy covers all of them.

Existing endpoint: POST int/v1/actions/send-push-notification (the console's current "push notification" screen) now creates and sends a campaign. That means it's recorded, respects preferences, goes to the inbox too, and is delivered in queued batches. The response keeps status, sent_count and total (now the number targeted) and adds campaign. PromotionalPushNotification is no longer used internally and is marked @deprecated.

Related Issue

Part of the promotions / notifications work. No tracking issue.

Type of Change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Test
  • Chore

Implementation Notes

Validation

  • Tests
  • Lint
  • Build (no frontend changes)
  • Manual validation
composer test:unit -> 582 tests, 3510 assertions, 0 failures (this branch, including #102-#104)
composer test:lint -> Found 0 of 311 files that can be fixed

New: server/tests/Unit/Promotions/CampaignDeliveryTest.php (every segment rule, network segments, due dispatching and batching, claim idempotency, recipient narrowing with an empty list reaching nobody, cancellation reasons, batch failure isolation, the command, the notification's channels, preferences, payload and storefronts) and CampaignEndpointsTest.php (send, cancel, audience, segment previews, announce, validation, filters, resources, the reworked push action).

Documentation Impact

  • No documentation changes needed
  • Documentation updated in fleetbase/fleetbase.io
  • Documentation needed but not included

API Reference Impact

  • No API reference changes needed
  • Updated fleetbase/postman
  • API reference updates required but not included

API reference notes: these are console (internal) endpoints only; there are no public API changes. The send-push-notification response adds campaign.

Documentation Notes

fleetbase.io, Storefront → Promotions → Segments and campaigns (rules, scheduling, announcing promotions, preferences).

Risk

  • Queue required: delivery is queued (SendCampaignBatch), so a queue worker must run. With the sync driver it sends inline as before.
  • Scheduler required: the scheduler must run for scheduled campaigns (storefront:dispatch-campaigns every minute).
  • The existing push action now also writes inbox notifications and skips customers who opted out of promotions.
  • min_spent sums storefront transactions across the whole company, not per store.

…campaigns

# Conflicts:
#	server/src/routes.php
Customer segments are rule based audiences of a store's or network's
customers (order count bounds, ordered or lapsed within N days, joined
within N days, minimum spend, reachable by push, explicit customers).

Campaigns send a title/body with an optional image and deep link to a
segment (or explicit recipients, or everyone) by push and/or inbox, now
or at a scheduled time. storefront:dispatch-campaigns claims due
campaigns atomically and queues delivery in batches; customers who
turned promotions off are skipped. Promotions can be announced with a
campaign that goes out when the promotion starts, and announcements for
promotions that stopped running are canceled.

The console API manages segments (with live audience previews) and
campaigns (send now, cancel, audience count). The existing promotional
push action now sends a tracked campaign.
@roncodes roncodes added needs-docs Requires documentation updates type:feature Feature or enhancement labels Sep 26, 2026
@codecov

codecov Bot commented Sep 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (490f9e5) to head (971427d).
⚠️ Report is 13 commits behind head on release/v0.4.22.

Additional details and impacted files
@@                  Coverage Diff                  @@
##             release/v0.4.22      #105     +/-   ##
=====================================================
  Coverage             100.00%   100.00%             
- Complexity              1772      2281    +509     
=====================================================
  Files                    135       182     +47     
  Lines                   7778      9070   +1292     
=====================================================
+ Hits                    7778      9070   +1292     
Flag Coverage Δ
backend 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@roncodes
roncodes merged commit efd80ad into release/v0.4.22 Sep 28, 2026
@roncodes
roncodes deleted the feat/promotion-campaigns branch September 28, 2026 04:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-docs Requires documentation updates type:feature Feature or enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant