commercetools

TL;DR

The webshop platform, and the only place a discount's reason survives, because the middleware drops it before NetSuite.

The webshop platform. CCC takes discount information from it: the cart discounts that exist, and which of them came off each sales order.

Why CCC reads it at all

NetSuite already carries the money. What it does not carry is why an order was discounted: the middleware between the webshop and NetSuite drops the discount reason, so NetSuite never receives it.

commercetools is the only place that reason exists. That is the whole justification for this source. Without it a discounted order shows a smaller amount with no way to attribute it to a campaign, a code or a rule.

How CCC reaches it

A GraphQL API with its own client model, authenticated by a token CCC caches in the tokens table under commercetools. The token lives 48 hours and is refreshed at 24, half its life, so a refresh failure has a day of slack before anything breaks.

Its credentials use the CTP_* prefix in .env, which is the platform's own convention rather than the source-name pattern the other groups follow. The scopes requested are read-only: viewing orders, discount codes and cart discounts.

What it delivers

FeedGrainNotes
Cart discountsOne per configured discountThe definitions, with their localised names
Order cart discountsOne row per order and cart discountSummed from line-item discounted quantities

The second is more detailed than it first looks. A discount is resolved down to the discounted price per quantity on each line item, and the discount codes that triggered each cart discount are collected per order. So one order can carry several discounts, and one discount can be reached through several codes.

Cadence and window

The definitions refresh less often than the applications, which follow orders closely.

It re-reads a trailing window rather than each day once. The order discount feed is one of the two sources ingest.trailingqueuedays re-queues, currently 14 days, because the webshop keeps adjusting recent days. A discount figure from last week can therefore still move. See configuration and boundaries.

The client requests orders by createdAt for the batch date. Earlier days can be queued manually; this does not guarantee that the upstream platform retains every historic order.

Quirks and limits

  • A discount code is a voucher, not an identity. It does not identify who used it, and reading it as though it does is wrong in a way that looks plausible.
  • A cart discount can have no name, and the import counts those separately rather than discarding them. A nameless discount is real data, not a fault.
  • Names are localised, so a single discount carries several and one has to be chosen for display.
  • The discount CCC records is what the webshop applied. It is not the same number as the revenue reduction booked in the ledger, which arrives through NetSuite. Do not expect the two to reconcile line for line: CCC does not reconcile them.

Where it lands

ct_-prefixed tables. ct_cartdiscounts is refreshed by upsert rather than by the swap pattern the other reference imports use, which is why it is defined in the installer like an ordinary table rather than by its own job.

Classification reads the imported applications and sets discountcode when a code is present, or discountcart when it is absent. Both can be set on one order. These flags reach rule filters; the detailed promotion amounts and definitions are not themselves rule-builder fields. An ambiguous code stored as NULL therefore contributes to discountcart. Transaction discount lines remain the monetary input, separate from these promotion flags.

Evidence

Client, token lifetime and scopes from app/models/commercetools.php. Feed shapes and the per-quantity resolution from app/jobs/commercetools/getcartdiscounts.php and getordercartdiscounts.php. The dropped discount reason, the CTP_* prefix, the token half-life and the upsert-not-swap decision from AGENTS.md. Trailing window from config/settings.json. Basis: code-checked against the current implementation.

Only the trailing window is re-queued, so a day older than that is refreshed by queueing it by hand. The webshop discount and ledger revenue are not reconciled anywhere in CCC: the discount reason is carried for analysis, and the amounts that reach margin are the transaction's own discount lines.

About this document
  • Type
    source
  • Status
    active
  • Updated
    2026-09-14
  • Created
    2026-09-13