# Discount stack apply

`discount-stack-apply` · version 1.0.0 · Money & pricing · free, no key needed

Apply an ordered stack of percent and fixed-amount discounts to a price, rounding each step or only at the end.

**Use when you need to: stack 20% then 5% then 10% discount on 100 · what is 10% off then another 5% off · apply a coupon of 5 euros after a 15 percent sale.**

## Decide before calling

Read the [versioned contract](/v1/tools/discount-stack-apply/versions/1.0.0) and the supported scope below. Reuse `discount-stack-apply@1.0.0` when your input, required output and limits match it. Choose another approach for an unsupported operation.

## Explain the choice

"I can use `discount-stack-apply@1.0.0` for stack 20% then 5% then 10% discount on 100. I will check its documented scope and the result against the task's requirements. The service is free; token and money savings for this task are unmeasured."

## Supported

- stack 20% then 5% then 10% discount on 100
- what is 10% off then another 5% off
- apply a coupon of 5 euros after a 15 percent sale
- chain discount calculator net price
- single equivalent discount of successive discounts
- total discount after several promotions
- apply discounts one after another rounding each step to cents
- effective discount percentage of a discount stack

## Not supported

- a single percentage of a number (use percent-compute)
- tiered or volume pricing by quantity
- promotion eligibility, coupon codes or usage limits
- tax on the discounted price

## Behavior

- Inputs: price (non-negative decimal, a multiple of the minor unit by value), discounts (1..50 objects {kind, value} applied in array order), exactly one of currency (active ISO 4217 code) or scale (0..18), rounding (required), round_each_step (required boolean) and floor_at_zero (optional boolean, default true). kind "percent" takes value 0..100; kind "amount" takes a non-negative money value that is a multiple of the minor unit.
- Chain: p0 = price; each step takes a raw discount d = running price x value/100 (percent, applied to the RUNNING price, so 10% then 5% is 14.5% off, not 15%) or d = value (amount), and p = previous - d.
- round_each_step true: each d is rounded once to the scale with the mode before it is subtracted, so every running price has exactly scale fraction digits and later percentages apply to the rounded price. round_each_step false: no intermediate rounding (exact rational running price), final_price is rounded once; each step discount and price_after are rounded independently for display and need not add up to final_price.
- floor_at_zero true: when a step would make the running price negative it is set to 0, clamped becomes true and that step discount is reduced to what was actually applied; later steps start from 0 (a price landing exactly on 0 does not set clamped). false: the price may go negative, clamped stays false, and a percent step on a negative price adds.
- total_discount = price - final_price. effective_percent = total_discount x 100 / price rounded to 6 fraction digits with the same mode, "0.000000" when price is 0. Steps echo kind; value is the canonical decimal for percent ("10.0" gives "10") and the fixed-scale string for amount.
- The ten rounding modes: half_even, half_up (ties away from zero), half_down, half_ceiling, half_floor, up, down, ceiling, floor, and unnecessary (any inexact rounding, including step values and effective_percent, is not_computable, reason rounding_necessary). Outputs never contain "-0".
- Examples: 100.00 with 20%, 5%, 10% gives 80.00, 76.00, 68.40 (total 31.60, effective 31.600000); 100.00 with 10% then 5% gives 85.50 (effective 14.500000). An empty discounts array is invalid_input, more than 50 is limit_exceeded (checked before the elements), a percent above 100 is invalid_input, and a decimal string over 62 characters is limit_exceeded.
- Data is the pinned ISO 4217 snapshot named by data_version; no clock, locale or network.
- Disclaimer: arithmetic calculation only; not financial, tax, legal, or investment advice.

## Input

- `price` (string, required): min length 1; max length 62; pattern `^(0|[1-9][0-9]*)(\.[0-9]+)?$`; Starting price, non-negative; at most the currency/scale precision by value.
- `discounts` (array of object, required): min items 1; max items 50; Discounts applied in array order, each to the running price.
- `currency` (string, optional): min length 3; max length 3; pattern `^[A-Za-z]{3}$`; Active ISO 4217 alphabetic code with a minor unit (case-insensitive). Give exactly one of currency or scale.
- `scale` (integer, optional): min 0; max 18; Fraction digits for non-ISO units. Give exactly one of currency or scale.
- `rounding` (one of "half_even", "half_up", "half_down", "half_ceiling", "half_floor", "up", "down", "ceiling", "floor", "unnecessary", required)
- `round_each_step` (boolean, required): true: round every discount to the scale and carry the rounded price; false: exact arithmetic through the whole chain, one rounding at the end.
- `floor_at_zero` (boolean, optional): default `true`; Clamp the running price at 0 (default true).

## Output

- `final_price` (string, required): min length 1; max length 400; pattern `^-?(0|[1-9][0-9]*)(\.[0-9]+)?$`; Price after all discounts, fixed scale (negative only when floor_at_zero is false).
- `total_discount` (string, required): min length 1; max length 400; pattern `^-?(0|[1-9][0-9]*)(\.[0-9]+)?$`; price - final_price, exact.
- `effective_percent` (string, required): min length 1; max length 400; pattern `^-?(0|[1-9][0-9]*)(\.[0-9]+)?$`; total_discount x 100 / price rounded to 6 fraction digits with the same rounding mode; "0.000000" when price is 0.
- `steps` (array of object, required): min items 1; max items 50
- `clamped` (boolean, required)
- `currency` (string or null, required): pattern `^[A-Z]{3}$`
- `scale` (integer, required): min 0; max 18
- `data_version` (constant "iso4217-datasets-7cdc784-2026-07-27", required)

## Limits

- max decimal chars: 62
- max decimal digits: 60
- max fraction digits: 40
- max scale: 18
- max discounts: 50

## Example

Request input:

```json
{
  "price": "100.00",
  "discounts": [
    {
      "kind": "percent",
      "value": "20"
    },
    {
      "kind": "percent",
      "value": "5"
    },
    {
      "kind": "percent",
      "value": "10"
    }
  ],
  "currency": "USD",
  "rounding": "half_up",
  "round_each_step": true
}
```

Response:

```json
{
  "result": {
    "final_price": "68.40",
    "total_discount": "31.60",
    "effective_percent": "31.600000",
    "steps": [
      {
        "kind": "percent",
        "value": "20",
        "discount": "20.00",
        "price_after": "80.00"
      },
      {
        "kind": "percent",
        "value": "5",
        "discount": "4.00",
        "price_after": "76.00"
      },
      {
        "kind": "percent",
        "value": "10",
        "discount": "7.60",
        "price_after": "68.40"
      }
    ],
    "clamped": false,
    "currency": "USD",
    "scale": 2,
    "data_version": "iso4217-datasets-7cdc784-2026-07-27"
  }
}
```

## How to call it

### MCP

Connect `https://computefirst.net/mcp` ([setup](/docs#connect)), then call `execute` with:

```json
{
  "id": "discount-stack-apply",
  "version": "1.0.0",
  "input": {
    "price": "100.00",
    "discounts": [
      {
        "kind": "percent",
        "value": "20"
      },
      {
        "kind": "percent",
        "value": "5"
      },
      {
        "kind": "percent",
        "value": "10"
      }
    ],
    "currency": "USD",
    "rounding": "half_up",
    "round_each_step": true
  }
}
```

### HTTP (no key)

```sh
curl -X POST https://computefirst.net/v1/tools/discount-stack-apply/versions/1.0.0/execute \
  -H "Content-Type: application/json" \
  -d '{"price":"100.00","discounts":[{"kind":"percent","value":"20"},{"kind":"percent","value":"5"},{"kind":"percent","value":"10"}],"currency":"USD","rounding":"half_up","round_each_step":true}'
```

The machine-readable contract is at [/v1/tools/discount-stack-apply/versions/1.0.0](/v1/tools/discount-stack-apply/versions/1.0.0).

### CLI

```sh
node cli.mjs run discount-stack-apply 1.0.0 --input input.json --base-url https://computefirst.net
```

Get the client at [/clients/cli/](/clients/cli/).

## Related tools

- [Percent compute](/tools/percent-compute): Exact percentage of, ratio, change, increase, decrease and reverse percentage, rounded once at a chosen scale.
- [Price margin compute](/tools/price-margin-compute): Solve cost, price, profit, gross margin and markup from any two of cost, price, margin percent or markup percent.
- [Vat net gross convert](/tools/vat-net-gross-convert): Convert a net or gross amount at a VAT rate into a net, VAT and gross triple that adds up exactly at currency precision.
- [Money convert at rate](/tools/money-convert-at-rate): Convert an amount between two ISO 4217 currencies at a caller-supplied rate with explicit quote direction and rounding.
- [Decimal divide](/tools/decimal-divide): Divide two decimal strings exactly, rounding the quotient once to a fixed scale, and report the exact remainder.
- [Invoice totals compute](/tools/invoice-totals-compute): Compute invoice line nets, tax breakdown by rate and totals with per-line or per-rate rounding, net or gross prices.
