Discounts — Reference
The Discounts surface is the automatic-pricing plane for SGEN commerce. It owns the rules that lower a cart's total without the customer entering a code — quantity tiers, customer-segment offers, buy-one-get-one bundles, and condition-driven price drops that fire when the cart shape matches.
This page is a reference for platform engineers and integrators who need to understand the surface before extending it, scripting against it, or scoping a promotion model. Customer-facing how-tos live in the customer docs set; this page describes the shape of the surface, not the steps to drive it.
Overview
Discounts lives under the Commerce module in SG-Admin. The module renders three primary views — the rule list, the rule create / edit form, and the run-history view with one entry per cart that triggered the rule — and exposes a small set of write operations for create, edit, pause, resume, archive, and reorder rule priority.
The module is paired by convention: one half renders the views and prepares the rule and history data, the other half handles writes and returns AJAX responses. Engineers reading the SG-Admin source will see this split across two controller files; the reference below describes the combined surface as it appears to a calling integration.
Discounts are distinct from Coupons. A coupon requires the customer to enter a code at checkout; a discount rule fires automatically when its condition matches. Both surfaces share the underlying ledger of cart adjustments, but the entry points are different. Coupon rules live under the adjacent Coupons surface; this page covers automatic discounts only.
Where it lives in SG-Admin:
- Sidebar: SG-Admin → Commerce → Discounts
- URL prefix:
/sg-admin/discounts/ - View templates:
application/views/Admin/Commerce/Discounts/
┌──────────────────────────────────────────────────────────────────────┐│ SG-Admin → Commerce → Discounts [ + Add rule ] │├──────────────────────────────────────────────────────────────────────┤│ Name Type Trigger Status ││ ───────────────────── ────────── ──────────────────── ────────────││ Buy 2 get 1 free BOGO cart_qty >= 3 active ││ 10% off first order segment customer.new active ││ Free shipping > $75 threshold cart_subtotal >= 75 active ││ Tier 5 — $20 off quantity cart_qty >= 5 scheduled ││ Loyal customer 15% segment orders_lifetime >= 5 paused ││ ││ Fired (30d): 1,284 carts · Discounted (30d): $12,840.50 ││ [ Edit ] [ Pause ] [ Reorder ] [ Archive ] │└──────────────────────────────────────────────────────────────────────┘Actions
The Discounts surface exposes the following operations. Each is described by what it does to the data, not by its internal method name.
List discount rules
Returns the rule set paginated, with name, type (quantity / segment / BOGO / threshold), trigger summary, status (active, paused, scheduled, expired, archived), and a fire count. Supports filter by status, by type, by date range, and by trigger field. The default view excludes archived rules — they remain queryable via the explicit filter.
Engineering note. The list view does not compute the trigger preview live. The trigger summary is rendered from the stored condition expression at write time and refreshed on edit.
View discount rule
Returns the full record for a single rule — name, type, condition expression, discount value, schedule window, customer-segment scope, product-scope filter, stacking policy, and the run-history ledger with one entry per cart that triggered the rule. The history is paginated; the total fire count and total discounted amount are summarized at the top.
Create discount rule
Opens an empty rule form. Required fields at minimum: name, type, condition expression, discount value or BOGO ratio. Optional fields cover schedule window (start / end timestamp), customer-segment filter, product-scope filter, stacking policy against other discounts, per-customer cap, and per-order cap. On submit, the surface validates the condition expression against the rule grammar, persists the record with an evaluation priority slot, and returns the new identifier.
Edit discount rule
Loads an existing record into the same form shape used for create, pre-populated. Submit replaces the stored rule with the posted values; fields left blank do not clear server-side values (the form is partial-update by default). Edits to an active rule take effect on the next cart evaluation — in-flight carts already at checkout retain the previously applied amount.
Pause and resume
Toggles the rule between active and paused without altering its definition. Paused rules are excluded from cart evaluation but retain their run history and remain queryable. Resume restores the rule to active without resetting any caps.
Reorder priority
The Discounts surface evaluates rules in a priority order set by the operator. Reorder rewrites the evaluation slot for a given rule, shifting the rest of the list to accommodate. Priority matters when stacking is disabled — the first matching rule wins, and later rules are skipped for that cart. Priority is read-only when stacking is enabled across all rules.
Archive rule
Marks the rule inactive without removing it from the database. Archived rules disappear from the default list view but remain queryable via the archived filter. Their run-history entries remain attached for audit; the rule itself is excluded from cart evaluation.
View run history
Returns the per-cart ledger for a single rule. Each entry records the cart identifier, the customer, the matched condition value, the discount amount applied, the timestamp, and the order outcome (paid, abandoned, refunded). Used for promotion attribution and for diagnosing rules that fire less often than expected.
Engineering note. The run-history table is append-only. Reversals on refund write a separate reversing entry rather than rewriting the original.
Data model
A discount rule record carries the following fields. Field names below are the conceptual shape — the on-disk column names match closely but are not contractually stable across releases.
| Field | Type | Notes |
|---|---|---|
id | integer | Primary key. Stable across edits. |
name | string | Operator-facing label. Not unique. |
type | enum | quantity, segment, bogo, threshold, custom. |
condition | expression | Stored expression evaluated against cart shape. |
value | numeric | Discount amount or percent. Sign is positive. |
value_kind | enum | flat, percent, bogo_ratio. |
priority | integer | Evaluation slot. Lower runs first. |
status | enum | active, paused, scheduled, expired, archived. |
starts_at | timestamp | Optional schedule start. |
ends_at | timestamp | Optional schedule end. |
segment_filter | json | Customer-segment selector. Empty means all customers. |
product_filter | json | Product / collection scope. Empty means all products. |
stack_policy | enum | exclusive, stackable, stack_with_coupon_only. |
cap_per_customer | integer | Optional max fires per customer. |
cap_per_order | integer | Optional max applications per cart. |
created_at | timestamp | Set on create, immutable. |
updated_at | timestamp | Touched on any edit. |
rule_id, cart_id, customer_id, matched_value, discount_applied, fired_at, order_outcome. Reversals on refund write a separate reversing entry referencing the original.Condition semantics: the expression is evaluated at cart-evaluation time, not at write time. Cart shape (items, quantities, customer attributes, order count) is passed in; the rule returns a boolean. Conditions reference cart fields by stable slugs — cart_qty, cart_subtotal, customer.new, orders_lifetime, item.collection, and so on.
Stacking semantics: exclusive rules block all other discounts on the same cart once they fire. stackable rules combine additively with other stackable rules and with coupons. stack_with_coupon_only permits coupons but blocks other automatic discounts.
CART│▼┌───────────────────────────────────────────┐│ Discount engine ││ ││ for each active rule (priority order): ││ ▶ schedule window OK? ││ ▶ segment filter matches customer? ││ ▶ product filter matches cart items? ││ ▶ caps not yet reached? ││ ▶ condition expression returns true? ││ ││ if YES → apply discount + write history ││ if stack_policy = exclusive → stop ││ else → continue to next rule │└───────────────────────┬───────────────────┘│▼ADJUSTED TOTALPermissions
Access to the Discounts surface is gated at two layers.
Layer 1 — admin gate. Every action under SG-Admin passes through the platform's standard admin access check at request entry. An unauthenticated request never reaches the Discounts surface.
Layer 2 — per-action capability. Within SG-Admin, each Discounts action checks a capability associated with the calling operator's role. The default role configuration ships with three roles — Administrator, Editor, Viewer — and the capability map is:
| Capability | Administrator | Editor | Viewer |
|---|---|---|---|
| List discount rules | ✔ | ✔ | ✔ |
| View rule | ✔ | ✔ | ✔ |
| View run history | ✔ | ✔ | ✔ |
| Create rule | ✔ | ✔ | — |
| Edit rule | ✔ | ✔ | — |
| Pause / resume | ✔ | ✔ | — |
| Reorder priority | ✔ | — | — |
| Adjust caps mid-flight | ✔ | — | — |
| Archive rule | ✔ | — | — |
| Restore archived rule | ✔ | — | — |
Self-protection rules. A rule cannot be edited mid-evaluation. The surface returns a structured rejection if a write lands during a cart-evaluation hold. An operator cannot archive a rule that has fired in the current pay period without explicit confirmation — the audit trail must remain consistent with the period's order ledger.
Audit. Every write — create, edit, pause, resume, reorder, archive, restore — emits an Activity Log entry. The log records the acting operator, the rule identifier, and the change shape (field-level diff for edits). Rule fires also write to the run-history ledger; the two records are correlated by rule identifier and timestamp.
REQUEST│▼┌───────────────────────────┐│ Admin gate │ unauth → reject│ (every /sg-admin/* call) │└─────────────┬─────────────┘│ authed▼┌───────────────────────────┐│ Capability check │ role lacks cap → reject│ (per-action) │ (custom roles override defaults)└─────────────┬─────────────┘│ allowed▼┌───────────────────────────┐│ Self-protection rules │ mid-evaluation lock /│ │ active-period archive → reject└─────────────┬─────────────┘│ passes▼action executes│▼Activity Log entry + run historyRelated references
- Coupons — Reference. Code-driven counterpart. Same underlying cart-adjustment ledger; different entry point. Coupons require customer entry; discounts fire automatically.
- Gift Cards — Reference. Prepaid-credit plane. Applied alongside discounts at checkout. Stacking with discount rules is governed by the
stack_policyfield. - Abandoned Cart — Reference. Recovery emails may carry a one-time discount code generated from the Discounts engine. The handoff is a coupon record scoped to the recovery URL.
- Orders — Reference. Records the applied discount amount per line item. Refunds write reversing entries that propagate back to the rule's run history.
- Customers — Reference. Customer-segment filters reference attributes defined here — order count, lifetime value, tag set, signup window.
- Products — Reference. Product-scope filters reference collection identifiers and product attributes defined here.
- Settings — Reference. Owns the rule grammar registration, the stacking defaults, and the audit-log retention window. Changes there reshape the Discounts evaluation behavior.
/docs/discounts.