Discounts

TL;DR

Two different things carry that name. The discount lines on a transaction are the money taken off, and the commercetools cart discounts are the promotions that explain why.

A discount in CCC is recorded twice, from two systems, and the two answer different questions.

QuestionWhere the answer isFrom
How much came off this order?salesorderdiscount, and the same table per transaction typeNetSuite
Which promotion took it off, and under which code?ct_cartdiscounts and salesordercartdiscountscommercetools

The discount reason exists only in commercetools. The middleware between the webshop and NetSuite drops it, so NetSuite never receives it and no amount of work on the NetSuite side can recover it. That is the whole reason commercetools is a source at all.

The transaction discount lines

salesorderdiscount and its siblings on the other transaction types hold the discount lines as they arrived from NetSuite: the amount, against which line, on which transaction. They are part of the transaction and are what the margin arithmetic sees. They carry no reason.

The commercetools cart discounts

ct_cartdiscounts is the catalogue of promotions: the discount's own id and key, its name, whether it requires a code, its stacking mode, its validity window, whether it is active, and when CCC first and last saw it. app/jobs/commercetools/getcartdiscounts.php refreshes it by upsert, which is why it is a normal table in install.php rather than one of the swap-pattern tables.

salesordercartdiscounts is what each promotion actually took off each order: app/jobs/commercetools/getordercartdiscounts.php reads a day of orders, sums the discounted amount per cart discount across the order's line items, and writes one row per promotion per order with the amount, the currency and the code used. It deletes the order's existing rows first, so re-running a day replaces that day rather than doubling it.

The imported amount is centAmount × discounted quantity / 100, summed per cart discount and order, with its currency retained. It is not automatically an amount in euro.

Two details are worth knowing before reading these rows:

  • The code is null when it is undecidable. If two discount codes map to the same cart discount on one order, there is no single answer to which code caused it, and the job writes null rather than picking one.
  • Three discount shapes are not read: a direct discount, a discount on the order total, and a discount on shipping. The job prints the order number when it meets one, so the gap is visible in the run output rather than silent.

A discount code is not a person. A code identifies the promotion that was used, and the same code is used by many people.

Where to look

The discount lines appear on /transactions/salesorderdetail under the order's lines. The commercetools tables have no screen of their own and are read directly.

Evidence

The middleware dropping the reason from AGENTS.md, The application. Cart discount fields and the upsert from app/jobs/commercetools/getcartdiscounts.php and the CREATE TABLE in app/jobs/install/install.php. Per-order aggregation, the delete-then-write, the undecidable code and the three unread shapes from app/jobs/commercetools/getordercartdiscounts.php. Basis: code-checked at 30f5313.

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