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
- Clamp/validate the discount so it never exceeds unitPrice * units at the call site before constructing the OrderItem.
- Normalize discount representation: if discounts are percentages, compute absolute = subtotal * pct/100 and cap at subtotal.
- Reject the order line with a 400 when the supplied discount is greater than the line total rather than letting it reach the domain.
- 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
- Cap discount at the line subtotal before constructing OrderItem.
- Keep discount representation consistent (absolute vs percentage).
- Validate promo results against the line total in the promotion engine.
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
- Discount is not valid
- Invalid number of units
- Invalid units
- Is not possible to change the order status from {OrderStatus
- Empty stock, product item {Name} is sold out
AI-assisted analysis of dotnet/eShop@9b4f9434f4 (2026-08-13).
Data as JSON: /api/errors/98c83c3be450a9c0.
Report an issue: GitHub.