On this page

Pricing an order

Trace pricing priority, absence and the selected result; a short conditional is not the whole policy. See readability under real business rules for policy branches, initialization and coordination.

A checkout service charges Standard customers 3%, Preferred customers 1% and Staff customers nothing. A promotion halves that fee when the order is at least 100 currency units. The final fee is rounded to two decimal places using midpoint-to-even rounding.

Every implementation rejects a negative amount and an unknown tier. Zero is a valid amount. The promotion boundary is inclusive. There is no tax, exchange-rate conversion or database access in this calculation.

The action and methods below belong inside the application's pricing class or module. The Python function is module-level. The tier is an integer supplied by a boundary layer: 0 is Standard, 1 is Preferred and 2 is Staff. Explicit validation is necessary because a caller can supply an unsupported integer in every version.

DirectSharp

// Pricing policy: preserve the order of guards and selected calculations.
action CalculateFee(amount as Decimal, tier as Integer, promotion as Boolean)
    returns Decimal

    require amount >= 0 otherwise new Global.System.ArgumentOutOfRangeException("amount")

    let rate as Decimal = select tier
        case 0
            take 0.03
        case 1
            take 0.01
        case 2
            take 0
        else
            throw new Global.System.ArgumentOutOfRangeException("tier")
    end select

    let fee as Decimal = amount * rate
    if promotion and amount >= 100 then
        fee = fee / 2
    end if

    return Global.System.Decimal.Round(
        fee, 2, Global.System.MidpointRounding.ToEven)
end action

require makes the entry condition explicit. otherwise supplies the failure expression. Expression-valued select supplies the rate through take; return exits the action. The exceptional case does not need to produce a rate because it cannot complete normally. Decimal context gives the rate literals their intended type.

C#

// Pricing policy: preserve the order of guards and selected calculations.
using System;

public static decimal CalculateFee(decimal amount, int tier, bool promotion)
{
    if (amount < 0)
        throw new ArgumentOutOfRangeException(nameof(amount));

    decimal rate = tier switch
    {
        0 => 0.03m,
        1 => 0.01m,
        2 => 0m,
        _ => throw new ArgumentOutOfRangeException(nameof(tier))
    };

    decimal fee = amount * rate;
    if (promotion && amount >= 100m)
        fee /= 2m;

    return decimal.Round(fee, 2, MidpointRounding.ToEven);
}

The switch expression performs the same selection. The m suffix fixes a literal's decimal type. C# expresses the action result in the method header and uses braces for its body. The explicit argument guard carries the same application rule as DirectSharp's contract.

VB.NET

' Pricing policy: preserve the order of guards and selected calculations.
Option Strict On
Option Explicit On
Option Infer On
Imports System

Public Function CalculateFee(amount As Decimal, tier As Integer,
                             promotion As Boolean) As Decimal
    If amount < 0D Then
        Throw New ArgumentOutOfRangeException(NameOf(amount))
    End If

    Dim rate As Decimal
    Select Case tier
        Case 0
            rate = 0.03D
        Case 1
            rate = 0.01D
        Case 2
            rate = 0D
        Case Else
            Throw New ArgumentOutOfRangeException(NameOf(tier))
    End Select

    Dim fee As Decimal = amount * rate
    If promotion AndAlso amount >= 100D Then
        fee /= 2D
    End If

    Return Decimal.Round(fee, 2, MidpointRounding.ToEven)
End Function

Select Case assigns the same local rather than producing an expression result. AndAlso is the short-circuit Boolean operator. Using And instead can change evaluation behavior when the right side contains a call, even though this particular numeric comparison has no effect.

Python

# Pricing policy: preserve the order of guards and selected calculations.
from decimal import Decimal, ROUND_HALF_EVEN


def calculate_fee(amount: Decimal, tier: int, promotion: bool) -> Decimal:
    if amount < 0:
        raise ValueError("amount must be nonnegative")

    match tier:
        case 0:
            rate = Decimal("0.03")
        case 1:
            rate = Decimal("0.01")
        case 2:
            rate = Decimal("0")
        case _:
            raise ValueError("unknown tier")

    fee = amount * rate
    if promotion and amount >= Decimal("100"):
        fee /= Decimal("2")

    return fee.quantize(Decimal("0.01"), rounding=ROUND_HALF_EVEN)

match selects the same rate, and Decimal is constructed from text to avoid introducing binary floating-point error. Invalid values produce Python's ValueError; the .NET versions use ArgumentOutOfRangeException. These are the corresponding argument-failure categories, rather than identical exception types across runtimes.

The Python contract assumes a Decimal amount, an integer tier and a Boolean promotion flag. Those annotations are not runtime guards. Python's bool is also a subtype of int, so a caller passing True as the tier would match 1. A dynamically exposed API should validate its input types before calling this calculation.

Boundary cases

Input Fee or failure
Standard, 100, no promotion 3.00
Preferred, 100, promotion 0.50
Standard, 99.99, promotion 3.00; promotion does not apply
Staff, 500, either promotion flag 0.00
Standard, 0.50, no promotion 0.02; 0.015 rounds to the even cent
Any supported tier, 0 0.00
Negative amount Argument failure
Tier 3 Argument failure

These are financial examples with modest finite decimal values. Python's Decimal context and .NET Decimal have different precision, range and overflow behavior. Equivalent production boundaries must account for those differences, rather than assuming both numeric systems accept the same extreme inputs.

What changes for the reader

All four versions make the rules visible. DirectSharp's contract and result-producing select separate a precondition from a choice. C#'s switch expression is compact. VB.NET's named blocks and assignments are familiar to many .NET business developers. Python removes most type syntax, with more responsibility at dynamic input boundaries.

A typed application could replace the integer tier with an enum and still validate values arriving from serialization or imported CLR code. The integer representation here keeps that boundary explicit and avoids comparing a validated enum in one language with unrestricted input in another.

For several orders, an ordinary loop can call this calculation sequentially. DirectSharp uses for_each order in orders ... end for_each; C# uses foreach, VB.NET uses For Each ... Next, and Python uses for. Moving the calculation into a concurrent loop changes scheduling and error policy, even when every order's successful fee remains the same.

Next: constructing and validating a customer · All comparisons