dotnet/eShop · error · OrderingDomainException

The total of order item is lower than applied discount

Error message

The total of order item is lower than applied discount

What it means

Thrown by the OrderItem constructor when (unitPrice * units) < discount — i.e. the line discount exceeds the gross line total, which would produce a negative net price. The aggregate treats this as an invariant violation and refuses to construct the item. It is an OrderingDomainException.

Source

Thrown at src/Ordering.Domain/AggregatesModel/OrderAggregate/OrderItem.cs:32

    
    public decimal Discount { get; private set; }
    
    public int Units { get; private set; }

    public int ProductId { get; private set; }

    protected OrderItem() { }

    public OrderItem(int productId, string productName, decimal unitPrice, decimal discount, string pictureUrl, int units = 1)
    {
        if (units <= 0)
        {
            throw new OrderingDomainException("Invalid number of units");
        }

        if ((unitPrice * units) < discount)
        {
            throw new OrderingDomainException("The total of order item is lower than applied discount");
        }

        ProductId = productId;

        ProductName = productName;
        UnitPrice = unitPrice;
        Discount = discount;
        Units = units;
        PictureUrl = pictureUrl;
    }
    
    public void SetNewDiscount(decimal discount)
    {
        if (discount < 0)
        {
            throw new OrderingDomainException("Discount is not valid");
        }

View on GitHub (pinned to 9b4f9434f4)

Solutions

  1. Clamp/validate the discount so it never exceeds unitPrice * units at the call site before constructing the OrderItem.
  2. Normalize discount representation: if discounts are percentages, compute absolute = subtotal * pct/100 and cap at subtotal.
  3. Reject the order line with a 400 when the supplied discount is greater than the line total rather than letting it reach the domain.
  4. Add a unit test asserting OrderItem construction for discount == subtotal succeeds but discount > subtotal throws.

Example fix

// before
order.AddOrderItem(p.Id, p.Name, p.Price, discount, url, qty);

// after
var lineTotal = p.Price * qty;
var safeDiscount = Math.Min(discount, lineTotal);
order.AddOrderItem(p.Id, p.Name, p.Price, safeDiscount, url, qty);
Defensive patterns

Strategy: validation

Validate before calling

var lineTotal = unitPrice * units;
if (discount > lineTotal) discount = lineTotal; // or reject
order.AddOrderItem(productId, name, unitPrice, discount, url, units);

Type guard

static bool IsDiscountWithinLine(decimal unitPrice, int units, decimal discount) => discount <= unitPrice * units;

Try / catch

try {
    order.AddOrderItem(id, name, price, discount, url, units);
} catch (OrderingDomainException ex) when (ex.Message.Contains("lower than applied discount")) {
    // promo exceeded subtotal — clamp and retry, or reject the line
}

Prevention

When it happens

Trigger: Creating an OrderItem (via constructor or Order.AddOrderItem) where the discount value passed is larger than unitPrice * units. Common when a percentage discount is mis-encoded as an absolute amount (e.g. 10 meaning 10% applied as $10), or when a fixed promo exceeds the item price for low-quantity lines.

Common situations: Promotion engine emitting a discount > line subtotal; client sending discount in cents vs dollars unit mismatch; a 100%-or-more coupon applied to a single low-price unit; tests hard-coding a discount without scaling to quantity.

Related errors


AI-assisted analysis of dotnet/eShop@9b4f9434f4 (2026-08-13). Data as JSON: /api/errors/98c83c3be450a9c0. Report an issue: GitHub.