{"id":1112,"date":"2026-02-24T18:43:43","date_gmt":"2026-02-24T18:43:43","guid":{"rendered":"https:\/\/inphronesys.com\/?p=1112"},"modified":"2026-02-24T18:43:43","modified_gmt":"2026-02-24T18:43:43","slug":"quantity-discount-analysis-the-hidden-trap-in-supplier-pricing-that-most-buyers-miss","status":"publish","type":"post","link":"https:\/\/inphronesys.com\/?p=1112","title":{"rendered":"Quantity Discount Analysis: The Hidden Trap in Supplier Pricing That Most Buyers Miss"},"content":{"rendered":"<h2>The Quote That Looks Too Good to Refuse<\/h2>\n<p>You&#8217;re a procurement manager at a mid-size electronics manufacturer. It&#8217;s Tuesday morning, and your inbox contains two quotes for a critical component \u2014 a custom circuit board that goes into your best-selling product. Supplier A offers $80 per unit for small orders, dropping to $69 for quantities above seven. Supplier B quotes $82 at the low end but offers $66 per unit for larger volumes.<\/p>\n<p>Your boss glances at the numbers and says: &#8222;Go with Supplier B at the top tier \u2014 $66 beats $69, and we save three dollars per board.&#8220;<\/p>\n<p>It sounds logical. It&#8217;s also wrong.<\/p>\n<p>Not because $66 isn&#8217;t less than $69 \u2014 basic arithmetic still works on Tuesdays. The problem is that quoted unit prices are not what you actually pay per <em>additional<\/em> unit when you step into a higher tier. In all-units discount schedules (by far the most common structure in B2B procurement), crossing a price break changes the cost of <em>every<\/em> unit in the order, not just the ones above the threshold. This creates a hidden cost landscape where the cheapest-looking quote can be the most expensive choice, and where ordering more units can actually raise your average cost.<\/p>\n<p>Quantity Discount Analysis (QDA) is the technique that unpacks this. And once you see how it works, you&#8217;ll never look at a supplier quote the same way again.<\/p>\n<h2>What Is Quantity Discount Analysis?<\/h2>\n<p>QDA is a structured method for evaluating supplier quantity discount schedules by computing the <em>true incremental cost<\/em> of each additional unit at every tier break. Instead of comparing quoted prices \u2014 the headline numbers suppliers put in front of you \u2014 QDA compares what you actually pay for the next batch of units when you move from one tier to the next.<\/p>\n<p>There are two fundamentally different discount structures, and confusing them is the first mistake most buyers make:<\/p>\n<h3>All-Units Discounts<\/h3>\n<p>The discounted price applies to <em>every<\/em> unit in the order, not just those above the breakpoint. If the price drops from $80 to $70 at quantity 4, all units cost $70 \u2014 including the first three. This is the dominant structure in B2B procurement.<\/p>\n<p><strong>Example:<\/strong> 6 units at $70 each = $420 total. Not $240 (first 3 at $80) + $210 (next 3 at $70).<\/p>\n<h3>Incremental (Tiered) Discounts<\/h3>\n<p>Each tier&#8217;s price applies only to the units <em>within<\/em> that tier. The first 3 units cost $80 regardless of how many more you order. Units 4 through 6 cost $70 each. Units 7 through 10 cost $69 each. This is common in SaaS pricing and consumer e-commerce but less prevalent in industrial procurement.<\/p>\n<p>The distinction matters enormously. In an all-units scheme, crossing a price break creates a <em>discontinuity<\/em> in the total cost curve \u2014 total cost can actually <em>decrease<\/em> when you order one more unit, because the lower price retroactively applies to all preceding units. This is where the hidden traps and opportunities live.<\/p>\n<p>For the rest of this post, we&#8217;ll focus on all-units discounts, because that&#8217;s what most procurement professionals deal with daily.<\/p>\n<h2>The Math: Average Price vs. Incremental Cost<\/h2>\n<p>Let&#8217;s work through Supplier A&#8217;s quote step by step.<\/p>\n<p><strong>Supplier A&#8217;s discount schedule:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">Tier<\/th>\n<th style=\"text-align: center;\">Quantity Range<\/th>\n<th style=\"text-align: center;\">Quoted Price\/Unit<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1<\/td>\n<td style=\"text-align: center;\">1\u20133 units<\/td>\n<td style=\"text-align: center;\">$80.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">2<\/td>\n<td style=\"text-align: center;\">4\u20136 units<\/td>\n<td style=\"text-align: center;\">$70.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">3<\/td>\n<td style=\"text-align: center;\">7\u201310 units<\/td>\n<td style=\"text-align: center;\">$69.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The quoted prices look clean and steadily declining. Now let&#8217;s compute what actually happens to your total cost at each tier boundary, and \u2014 critically \u2014 what each <em>incremental batch<\/em> of units costs you.<\/p>\n<p><strong>Total cost at tier boundaries:<\/strong><\/p>\n<ul>\n<li>At 3 units (top of Tier 1): 3 \u00d7 $80 = <strong>$240<\/strong><\/li>\n<li>At 6 units (top of Tier 2): 6 \u00d7 $70 = <strong>$420<\/strong><\/li>\n<li>At 10 units (top of Tier 3): 10 \u00d7 $69 = <strong>$690<\/strong><\/li>\n<\/ul>\n<p><strong>Incremental cost per unit for each tier step:<\/strong><\/p>\n<p>The incremental cost asks: &#8222;What did the additional units in this tier <em>actually<\/em> cost me, given the all-units price change?&#8220;<\/p>\n<ul>\n<li>Tier 1 \u2192 Tier 2: ($420 \u2212 $240) \/ 3 additional units = <strong>$60.00<\/strong> per incremental unit<\/li>\n<li>Tier 2 \u2192 Tier 3: ($690 \u2212 $420) \/ 4 additional units = <strong>$67.50<\/strong> per incremental unit<\/li>\n<\/ul>\n<p>Here&#8217;s the full picture:<\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">Tier<\/th>\n<th style=\"text-align: center;\">Qty Range<\/th>\n<th style=\"text-align: center;\">Quoted Price<\/th>\n<th style=\"text-align: center;\">Total Cost at Tier Max<\/th>\n<th style=\"text-align: center;\">Incremental Units<\/th>\n<th style=\"text-align: center;\">Incremental Cost\/Unit<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1<\/td>\n<td style=\"text-align: center;\">1\u20133<\/td>\n<td style=\"text-align: center;\">$80.00<\/td>\n<td style=\"text-align: center;\">$240<\/td>\n<td style=\"text-align: center;\">3<\/td>\n<td style=\"text-align: center;\">$80.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">2<\/td>\n<td style=\"text-align: center;\">4\u20136<\/td>\n<td style=\"text-align: center;\">$70.00<\/td>\n<td style=\"text-align: center;\">$420<\/td>\n<td style=\"text-align: center;\">3<\/td>\n<td style=\"text-align: center;\">$60.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">3<\/td>\n<td style=\"text-align: center;\">7\u201310<\/td>\n<td style=\"text-align: center;\">$69.00<\/td>\n<td style=\"text-align: center;\">$690<\/td>\n<td style=\"text-align: center;\">4<\/td>\n<td style=\"text-align: center;\">$67.50<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Look at that incremental cost column. The quoted price declines steadily: $80, $70, $69. But the incremental cost \u2014 what you actually pay for each additional unit \u2014 goes $80, $60, then <em>back up<\/em> to $67.50.<\/p>\n<p>The third tier&#8217;s quoted price is the lowest. Its incremental cost is <em>higher<\/em> than the second tier&#8217;s.<\/p>\n<p>This is not an error. It&#8217;s the mathematical consequence of all-units pricing. When you cross from Tier 2 to Tier 3, the price drops from $70 to $69 \u2014 just a $1 reduction. But that $1 discount now applies to all 10 units, saving you only $10 on the base while you&#8217;re committing to 4 additional units at the new price. The net effect: each of those 4 extra units costs you $67.50 in real incremental terms, not the $69 on the quote sheet.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_unit_vs_incremental.png\" alt=\"Quoted unit price versus incremental cost per tier for Supplier A\" \/><\/p>\n<h2>The Roller-Coaster Effect<\/h2>\n<p>When the incremental cost doesn&#8217;t decrease monotonically \u2014 when it drops, then rises \u2014 we get what&#8217;s called the <strong>roller-coaster effect<\/strong>. Unlike the amusement park version, nobody screams with delight on this ride. The chart below makes the pattern unmistakable.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_rollercoaster.png\" alt=\"Incremental cost across tiers showing the roller-coaster effect\" \/><\/p>\n<p>Why does this matter? Because the roller-coaster effect signals that the discount schedule&#8217;s structure is <em>punishing<\/em> you for ordering more. In Supplier A&#8217;s case, the jump from 6 to 10 units yields a worse per-unit deal than the jump from 3 to 6. A procurement professional looking only at quoted prices sees a consistent decline and concludes &#8222;more is cheaper.&#8220; The incremental cost analysis reveals the opposite.<\/p>\n<p>The roller-coaster effect is surprisingly common. It occurs whenever a tier boundary&#8217;s price drop is too small relative to the number of additional units required. Specifically, the incremental cost rises when:<\/p>\n<p><strong>IC_new &gt; IC_previous<\/strong><\/p>\n<p>where:<\/p>\n<p><strong>IC = (Q_new \u00d7 P_new \u2212 Q_prev \u00d7 P_prev) \/ (Q_new \u2212 Q_prev)<\/strong><\/p>\n<p>In procurement negotiations, spotting a roller-coaster pattern gives you concrete leverage: &#8222;Your Tier 3 pricing actually costs us <em>more<\/em> per incremental unit than Tier 2. Can you adjust the breakpoint or the price to fix this?&#8220;<\/p>\n<h2>Total Cost Perspective<\/h2>\n<p>The incremental cost analysis tells you about the <em>marginal<\/em> economics. But procurement decisions also depend on total spend, particularly when budgets are fixed or when you&#8217;re optimizing across multiple components.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_total_cost_curve.png\" alt=\"Total cost curve with tier breakpoints\" \/><\/p>\n<p>The total cost curve for an all-units discount schedule has a distinctive sawtooth shape. Within each tier, total cost rises linearly. At each tier boundary, there&#8217;s a sharp <em>downward<\/em> discontinuity \u2014 because the new, lower price retroactively applies to all units.<\/p>\n<p>This creates an interesting tactical opportunity: at certain quantities, ordering <em>one more unit<\/em> actually reduces your total cost. Right at the Tier 2 breakpoint, going from 3 units ($240) to 4 units ($280) costs you only $40 for that fourth unit \u2014 effectively a $40 unit when the quoted price is $70. At the Tier 3 breakpoint, going from 6 units ($420) to 7 units ($483) costs $63 for the seventh unit \u2014 below the $69 quoted price.<\/p>\n<p>Smart procurement teams use this insight to time purchases: if you need 3 units now and will need another 2 within the planning horizon, buying 6 upfront locks in the Tier 2 pricing and yields an incremental cost of just $60 per additional unit. Whether the carrying cost of those extra units justifies the savings is a classic EOQ trade-off \u2014 but now you have the real numbers to make that calculation.<\/p>\n<h2>Comparing Two Suppliers<\/h2>\n<p>Now let&#8217;s add Supplier B to the mix and see how the analysis changes everything.<\/p>\n<p><strong>Supplier B&#8217;s discount schedule:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">Tier<\/th>\n<th style=\"text-align: center;\">Quantity Range<\/th>\n<th style=\"text-align: center;\">Quoted Price\/Unit<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">1<\/td>\n<td style=\"text-align: center;\">1\u20133 units<\/td>\n<td style=\"text-align: center;\">$82.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">2<\/td>\n<td style=\"text-align: center;\">4\u20136 units<\/td>\n<td style=\"text-align: center;\">$72.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">3<\/td>\n<td style=\"text-align: center;\">7\u201310 units<\/td>\n<td style=\"text-align: center;\">$66.00<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Supplier B quotes higher prices for Tiers 1 and 2, but undercuts Supplier A on Tier 3 ($66 vs. $69). A quick comparison of quoted prices suggests Supplier B wins at high volumes and Supplier A wins at low volumes. The reality is more nuanced.<\/p>\n<p><strong>Side-by-side analysis:<\/strong><\/p>\n<table>\n<thead>\n<tr>\n<th style=\"text-align: center;\">Metric<\/th>\n<th style=\"text-align: center;\">Supplier A<\/th>\n<th style=\"text-align: center;\">Supplier B<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"text-align: center;\">Tier 1 quoted price<\/td>\n<td style=\"text-align: center;\">$80.00<\/td>\n<td style=\"text-align: center;\">$82.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Tier 2 quoted price<\/td>\n<td style=\"text-align: center;\">$70.00<\/td>\n<td style=\"text-align: center;\">$72.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Tier 3 quoted price<\/td>\n<td style=\"text-align: center;\">$69.00<\/td>\n<td style=\"text-align: center;\">$66.00<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Total cost at 3 units<\/td>\n<td style=\"text-align: center;\">$240<\/td>\n<td style=\"text-align: center;\">$246<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Total cost at 6 units<\/td>\n<td style=\"text-align: center;\">$420<\/td>\n<td style=\"text-align: center;\">$432<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Total cost at 10 units<\/td>\n<td style=\"text-align: center;\">$690<\/td>\n<td style=\"text-align: center;\">$660<\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Tier 2 incremental cost<\/td>\n<td style=\"text-align: center;\"><strong>$60.00<\/strong><\/td>\n<td style=\"text-align: center;\"><strong>$62.00<\/strong><\/td>\n<\/tr>\n<tr>\n<td style=\"text-align: center;\">Tier 3 incremental cost<\/td>\n<td style=\"text-align: center;\"><strong>$67.50<\/strong><\/td>\n<td style=\"text-align: center;\"><strong>$57.00<\/strong><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The incremental cost profiles are fundamentally different:<\/p>\n<ul>\n<li><strong>Supplier A<\/strong>: $80 \u2192 $60 \u2192 $67.50 (roller-coaster \u2014 incremental cost <em>rises<\/em> at Tier 3)<\/li>\n<li><strong>Supplier B<\/strong>: $82 \u2192 $62 \u2192 $57.00 (clean descent \u2014 each tier is genuinely cheaper)<\/li>\n<\/ul>\n<p>Supplier A&#8217;s Tier 3 discount is a mirage. The quoted price drops only $1 (from $70 to $69), which means each additional unit in the 7\u201310 range carries a higher incremental cost than units in the 4\u20136 range. Supplier B&#8217;s Tier 3 discount is real: the price drops $6 (from $72 to $66), enough to make each additional unit genuinely cheaper than the tier before.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_supplier_comparison.png\" alt=\"Supplier comparison: Supplier A with roller-coaster vs Supplier B with clean structure\" \/><\/p>\n<p>The strategic implications are clear:<\/p>\n<ul>\n<li><strong>For orders of 1\u20136 units<\/strong>: Supplier A is cheaper in total cost ($240 vs. $246 at 3 units; $420 vs. $432 at 6 units).<\/li>\n<li><strong>For orders of 7\u201310 units<\/strong>: Supplier B is cheaper ($660 vs. $690 at 10 units) <em>and<\/em> has a better incremental cost structure.<\/li>\n<li><strong>For scaling beyond 10 units<\/strong>: Supplier B&#8217;s clean downward trajectory suggests that higher tiers will continue to offer genuinely better value, while Supplier A&#8217;s roller-coaster pattern may recur.<\/li>\n<\/ul>\n<p>The recommendation? It depends on your volume. But the analysis gives you something a raw quote sheet never could: the ability to make that decision with numbers instead of intuition.<\/p>\n<h2>Using QDA in Negotiations<\/h2>\n<p>Quantity Discount Analysis doesn&#8217;t just help you choose between suppliers \u2014 it transforms how you negotiate with them. Here&#8217;s how procurement professionals use these insights at the table.<\/p>\n<h3>Expose the Roller-Coaster<\/h3>\n<p>Walk into the negotiation with your incremental cost chart printed on paper. (Yes, paper. Slides get clicked past; a single sheet on the table stays visible for the entire meeting.) Most sales representatives have never seen their own pricing analyzed this way. Saying &#8222;Your Tier 3 incremental cost is $67.50, which is <em>higher<\/em> than Tier 2&#8217;s $60.00 \u2014 can you explain why ordering more costs us more per unit?&#8220; puts the supplier on the defensive with math, not emotion.<\/p>\n<h3>Request Breakpoint Adjustments<\/h3>\n<p>If the roller-coaster effect stems from a tier boundary being in the wrong place, propose moving it. In Supplier A&#8217;s case, dropping the Tier 3 price from $69 to $66 \u2014 a $4 per-unit concession instead of $1 \u2014 would bring the Tier 3 incremental cost down to $60, matching Tier 2 exactly and eliminating the roller-coaster. That&#8217;s a concrete, data-backed ask that removes the structural penalty for ordering more.<\/p>\n<h3>Use Incremental Costs for Split-Award Strategies<\/h3>\n<p>If you need 10 units, consider a split award: buy 6 from Supplier A (exploiting their strong Tier 2 incremental cost of $60) and 4 from Supplier B at a negotiated spot price. Total cost: $420 + (4 \u00d7 negotiated price). If you can get Supplier B below $67.50 for those 4 units, you beat both suppliers&#8216; single-source 10-unit quotes.<\/p>\n<h3>Build Scenario Models<\/h3>\n<p>The R code below and the interactive dashboard let you plug in any supplier&#8217;s discount schedule and instantly see the incremental cost landscape. Run scenarios before every major negotiation: &#8222;What if the Tier 2 breakpoint moved from 4 to 5 units? What if Tier 3 pricing dropped another $2?&#8220; When your analysis is faster than the supplier&#8217;s, you control the conversation.<\/p>\n<h3>Connect to Total Cost of Ownership<\/h3>\n<p>Incremental cost analysis is one piece of the puzzle. For a complete picture, layer in carrying costs (warehousing those extra units), quality costs (does the cheaper supplier have a higher defect rate?), logistics costs (does one supplier offer better freight terms?), and lead time impacts. QDA gives you the procurement price truth; TCO gives you the business decision.<\/p>\n<h2>Key Takeaways<\/h2>\n<ol>\n<li><strong>Quoted prices lie by omission.<\/strong> All-units discount schedules create a gap between the listed price and the actual incremental cost. The only way to see the real economics is to compute the incremental cost at every tier boundary. A 2-minute spreadsheet calculation can save you thousands.<\/li>\n<li><strong>The roller-coaster effect is a red flag.<\/strong> When incremental costs rise at higher tiers, the discount structure is penalizing you for ordering more. This occurs when the price drop between tiers is too small relative to the number of additional units. The pattern is more common than most buyers expect \u2014 once you start checking, you&#8217;ll find it in a surprising share of the quotes sitting in your inbox right now.<\/li>\n<li><strong>Supplier comparison requires incremental analysis, not price-list scanning.<\/strong> Supplier A looks cheaper at low volumes; Supplier B looks cheaper at high volumes. But the incremental cost profiles reveal fundamentally different pricing philosophies: Supplier A front-loads the discount; Supplier B back-loads it. Your decision should depend on your actual order volumes, not on who has the lowest Tier 3 headline number.<\/li>\n<li><strong>Use the math in negotiations.<\/strong> Bringing incremental cost charts to a supplier meeting is the procurement equivalent of bringing data to a debate. Most suppliers don&#8217;t expect buyers to analyze their pricing this deeply. Those who do earn better terms.<\/li>\n<li><strong>Automate and explore.<\/strong> The R code and the interactive dashboard below let you run QDA on any discount schedule in seconds. Build it into your standard quote evaluation process \u2014 once it&#8217;s a habit, you&#8217;ll never miss a roller-coaster again.<\/li>\n<\/ol>\n<h2>Your Next Steps<\/h2>\n<ol>\n<li><strong>Pull your last three supplier quotes and compute incremental costs.<\/strong> It takes five minutes per quote. Use the formula: (Total cost at tier max \u2212 Total cost at previous tier max) \/ number of additional units. The R code below automates this, but a spreadsheet works fine too.<\/li>\n<li><strong>Flag any roller-coasters.<\/strong> If incremental cost rises at a higher tier, you&#8217;ve found a discount structure that penalizes you for ordering more. Highlight it \u2014 literally, in red \u2014 and bring it to your next supplier review meeting.<\/li>\n<li><strong>Reframe your next negotiation around incremental costs.<\/strong> Instead of the usual &#8222;Can you sharpen your pencil on pricing?&#8220;, try: &#8222;Your Tier 3 incremental cost is $67.50 \u2014 that&#8217;s <em>higher<\/em> than Tier 2&#8217;s $60.00. What can we do about that?&#8220; The specificity changes the dynamic entirely.<\/li>\n<li><strong>Build QDA into your standard quote evaluation process.<\/strong> The interactive dashboard below lets you plug in any discount schedule and see the incremental cost landscape instantly. Run it on every quote, and roller-coasters will never sneak past you again.<\/li>\n<\/ol>\n<h2>Interactive Dashboard<\/h2>\n<p>Explore the data yourself \u2014 plug in your own supplier quotes and see the incremental cost analysis update in real time.<\/p>\n<div class=\"dashboard-link\" style=\"margin: 2em 0; padding: 1.5em; background: #f8f9fa; border-left: 4px solid #0073aa; border-radius: 4px;\">\n<p style=\"margin: 0 0 0.5em 0; font-size: 1.1em;\"><strong>Interactive Dashboard<\/strong><\/p>\n<p style=\"margin: 0 0 1em 0;\">Explore the data yourself \u2014 adjust parameters and see the results update in real time.<\/p>\n<p><a style=\"display: inline-block; padding: 0.6em 1.2em; background: #0073aa; color: #fff; text-decoration: none; border-radius: 4px; font-weight: bold;\" href=\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/2026-02-24_Quantity_Discount_Analysis_dashboard.html\" target=\"_blank\" rel=\"noopener\">Open Interactive Dashboard \u2192<\/a><\/p>\n<\/div>\n<details>\n<summary><strong>Show R Code<\/strong><\/summary>\n<pre><code class=\"language-r\"># =============================================================================\n# Quantity Discount Analysis (QDA)\n# =============================================================================\n# A complete R script for analyzing supplier quantity discount schedules,\n# computing incremental costs, detecting roller-coaster effects, and\n# comparing suppliers side by side.\n#\n# To adapt to your own data:\n# 1. Modify the discount schedules in the DATA SETUP section below\n# 2. Each schedule is a data frame with columns: tier, qty_min, qty_max, price\n# 3. Run the full script \u2014 all charts update automatically\n#\n# Required packages: ggplot2, dplyr, tidyr, scales, patchwork\n# =============================================================================\n\nlibrary(ggplot2)\nlibrary(dplyr)\nlibrary(tidyr)\nlibrary(scales)\nlibrary(patchwork)\n\n# --- Theme for all plots ---\ntheme_qda &lt;- theme_minimal(base_size = 13) +\n  theme(\n    plot.title = element_text(face = \"bold\", size = 14),\n    plot.subtitle = element_text(color = \"grey40\", size = 11),\n    panel.grid.minor = element_blank(),\n    legend.position = \"bottom\"\n  )\n\n# =============================================================================\n# 1. DATA SETUP \u2014 Modify these for your own suppliers\n# =============================================================================\n\n# Supplier A: All-units discount schedule\nsupplier_a &lt;- data.frame(\n  supplier = \"Supplier A\",\n  tier = 1:3,\n  qty_min = c(1, 4, 7),\n  qty_max = c(3, 6, 10),\n  price = c(80, 70, 69)\n)\n\n# Supplier B: All-units discount schedule\nsupplier_b &lt;- data.frame(\n  supplier = \"Supplier B\",\n  tier = 1:3,\n  qty_min = c(1, 4, 7),\n  qty_max = c(3, 6, 10),\n  price = c(82, 72, 66)\n)\n\n# =============================================================================\n# 2. INCREMENTAL COST COMPUTATION\n# =============================================================================\n\ncompute_incremental_costs &lt;- function(schedule) {\n  # Computes the incremental cost per unit for each tier in an all-units\n\n  # discount schedule.\n  #\n  # Args:\n  #   schedule: data frame with columns tier, qty_min, qty_max, price\n  #\n  # Returns:\n  #   The input data frame with added columns:\n  #     total_cost  \u2014 total cost at the tier's maximum quantity\n  #     incr_units  \u2014 number of units added in this tier\n  #     incr_cost   \u2014 incremental cost per unit for this tier\n  #     roller_coaster \u2014 TRUE if incremental cost rises from previous tier\n\n  schedule &lt;- schedule %&gt;%\n    arrange(tier) %&gt;%\n    mutate(\n      # Total cost at the top of each tier (all-units pricing)\n      total_cost = qty_max * price,\n\n      # Number of incremental units in this tier\n      incr_units = qty_max - lag(qty_max, default = 0),\n\n      # Incremental cost per unit\n      incr_cost = (total_cost - lag(total_cost, default = 0)) \/ incr_units,\n\n      # Detect roller-coaster: incremental cost rises from previous tier\n      roller_coaster = incr_cost &gt; lag(incr_cost, default = Inf)\n    )\n\n  schedule\n}\n\n# Compute for both suppliers\nsa &lt;- compute_incremental_costs(supplier_a)\nsb &lt;- compute_incremental_costs(supplier_b)\n\n# Print results\ncat(\"=== Supplier A ===\\n\")\nprint(sa %&gt;% select(tier, qty_min, qty_max, price, total_cost, incr_cost, roller_coaster))\n\ncat(\"\\n=== Supplier B ===\\n\")\nprint(sb %&gt;% select(tier, qty_min, qty_max, price, total_cost, incr_cost, roller_coaster))\n\n# Check for roller-coaster\nif (any(sa$roller_coaster, na.rm = TRUE)) {\n  cat(\"\\n&#x26a0; Supplier A has a ROLLER-COASTER effect at tier(s):\",\n      sa$tier[sa$roller_coaster], \"\\n\")\n}\nif (any(sb$roller_coaster, na.rm = TRUE)) {\n  cat(\"\\n&#x26a0; Supplier B has a ROLLER-COASTER effect at tier(s):\",\n      sb$tier[sb$roller_coaster], \"\\n\")\n}\n\n# =============================================================================\n# 3. CHART 1 \u2014 Quoted Price vs. Incremental Cost (Supplier A)\n# =============================================================================\n\nsa_long &lt;- sa %&gt;%\n  select(tier, price, incr_cost) %&gt;%\n  pivot_longer(cols = c(price, incr_cost),\n               names_to = \"metric\",\n               values_to = \"value\") %&gt;%\n  mutate(metric = ifelse(metric == \"price\",\n                         \"Quoted Unit Price\",\n                         \"Incremental Cost\"))\n\np1 &lt;- ggplot(sa_long, aes(x = factor(tier), y = value, fill = metric)) +\n  geom_col(position = position_dodge(width = 0.6), width = 0.5) +\n  geom_text(aes(label = dollar(value, accuracy = 0.01)),\n            position = position_dodge(width = 0.6),\n            vjust = -0.5, size = 3.8, fontface = \"bold\") +\n  scale_fill_manual(values = c(\"Quoted Unit Price\" = \"#2980b9\",\n                                \"Incremental Cost\" = \"#e74c3c\")) +\n  scale_y_continuous(labels = dollar_format(), limits = c(0, 95)) +\n  labs(\n    title = \"Supplier A: Quoted Price vs. Incremental Cost\",\n    subtitle = \"The quoted price falls steadily \u2014 but incremental cost tells a different story\",\n    x = \"Discount Tier\",\n    y = \"Cost per Unit ($)\",\n    fill = NULL\n  ) +\n  theme_qda\n\nggsave(\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_unit_vs_incremental.png\", p1,\n       width = 8, height = 5, dpi = 100, bg = \"white\")\n\n# =============================================================================\n# 4. CHART 2 \u2014 Roller-Coaster Effect (Incremental Costs Only)\n# =============================================================================\n\nsa_rc &lt;- sa %&gt;%\n  mutate(\n    tier_label = paste0(\"Tier \", tier, \"\\n(\", qty_min, \"\u2013\", qty_max, \" units)\"),\n    fill_color = ifelse(roller_coaster, \"Increase (Roller-Coaster)\", \"Decrease\")\n  )\n\np2 &lt;- ggplot(sa_rc, aes(x = factor(tier), y = incr_cost, fill = fill_color)) +\n  geom_col(width = 0.5) +\n  geom_text(aes(label = dollar(incr_cost, accuracy = 0.01)),\n            vjust = -0.5, size = 4, fontface = \"bold\") +\n  # Add connecting line to show the roller-coaster shape\n  geom_line(aes(x = tier, y = incr_cost, group = 1),\n            linewidth = 1.2, color = \"grey30\", linetype = \"dashed\") +\n  geom_point(aes(x = tier, y = incr_cost), color = \"grey30\", size = 3) +\n  # Arrow annotation for the rise\n  annotate(\"segment\", x = 2.3, xend = 2.7, y = 62, yend = 66,\n           arrow = arrow(length = unit(0.2, \"cm\")), color = \"#e74c3c\", linewidth = 1) +\n  annotate(\"text\", x = 2.8, y = 64, label = \"Cost rises!\\nRoller-coaster\\neffect\",\n           color = \"#e74c3c\", size = 3.5, fontface = \"bold\", hjust = 0) +\n  scale_fill_manual(values = c(\"Decrease\" = \"#27ae60\", \"Increase (Roller-Coaster)\" = \"#e74c3c\")) +\n  scale_y_continuous(labels = dollar_format(), limits = c(0, 90)) +\n  scale_x_discrete(labels = c(\"Tier 1\\n(1\u20133 units)\", \"Tier 2\\n(4\u20136 units)\",\n                               \"Tier 3\\n(7\u201310 units)\")) +\n  labs(\n    title = \"The Roller-Coaster Effect: Supplier A's Incremental Costs\",\n    subtitle = \"Ordering more units in Tier 3 costs MORE per unit than Tier 2\",\n    x = NULL,\n    y = \"Incremental Cost per Unit ($)\",\n    fill = NULL\n  ) +\n  theme_qda\n\nggsave(\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_rollercoaster.png\", p2,\n       width = 8, height = 5, dpi = 100, bg = \"white\")\n\n# =============================================================================\n# 5. CHART 3 \u2014 Total Cost Curve with Tier Breakpoints\n# =============================================================================\n\n# Build a unit-by-unit total cost curve for Supplier A\nbuild_total_cost_curve &lt;- function(schedule, max_qty = NULL) {\n  if (is.null(max_qty)) max_qty &lt;- max(schedule$qty_max)\n\n  data.frame(qty = 1:max_qty) %&gt;%\n    rowwise() %&gt;%\n    mutate(\n      # Find which tier this quantity falls into\n      tier_idx = max(which(schedule$qty_min &lt;= qty)),\n      price = schedule$price[tier_idx],\n      total_cost = qty * price\n    ) %&gt;%\n    ungroup()\n}\n\ntc_a &lt;- build_total_cost_curve(supplier_a) %&gt;% mutate(supplier = \"Supplier A\")\ntc_b &lt;- build_total_cost_curve(supplier_b) %&gt;% mutate(supplier = \"Supplier B\")\n\n# Tier boundary annotations for Supplier A\ntier_breaks_a &lt;- data.frame(\n  qty = c(3, 6),\n  label = c(\"Tier 1\u21922\\nbreak\", \"Tier 2\u21923\\nbreak\")\n)\n\np3 &lt;- ggplot(tc_a, aes(x = qty, y = total_cost)) +\n  geom_line(linewidth = 1.2, color = \"#2980b9\") +\n  geom_point(size = 2.5, color = \"#2980b9\") +\n  # Mark the tier boundaries with vertical lines\n  geom_vline(xintercept = 3.5, linetype = \"dotted\", color = \"grey50\") +\n  geom_vline(xintercept = 6.5, linetype = \"dotted\", color = \"grey50\") +\n  # Highlight the discontinuity at tier breaks\n  annotate(\"point\", x = 4, y = 4 * 70, size = 4, color = \"#e74c3c\") +\n  annotate(\"point\", x = 3, y = 3 * 80, size = 4, color = \"#e74c3c\") +\n  annotate(\"segment\", x = 3, xend = 4, y = 3 * 80, yend = 4 * 70,\n           linetype = \"dashed\", color = \"#e74c3c\", linewidth = 0.8) +\n  annotate(\"text\", x = 3.5, y = 250, label = \"Only +$40\\nfor 4th unit\",\n           color = \"#e74c3c\", size = 3.2, fontface = \"italic\") +\n  annotate(\"point\", x = 7, y = 7 * 69, size = 4, color = \"#e74c3c\") +\n  annotate(\"point\", x = 6, y = 6 * 70, size = 4, color = \"#e74c3c\") +\n  annotate(\"segment\", x = 6, xend = 7, y = 6 * 70, yend = 7 * 69,\n           linetype = \"dashed\", color = \"#e74c3c\", linewidth = 0.8) +\n  annotate(\"text\", x = 6.5, y = 450, label = \"Only +$63\\nfor 7th unit\",\n           color = \"#e74c3c\", size = 3.2, fontface = \"italic\") +\n  # Tier labels\n  annotate(\"text\", x = 2, y = 680, label = \"Tier 1\\n$80\/unit\",\n           color = \"grey40\", size = 3) +\n  annotate(\"text\", x = 5, y = 680, label = \"Tier 2\\n$70\/unit\",\n           color = \"grey40\", size = 3) +\n  annotate(\"text\", x = 8.5, y = 680, label = \"Tier 3\\n$69\/unit\",\n           color = \"grey40\", size = 3) +\n  scale_x_continuous(breaks = 1:10) +\n  scale_y_continuous(labels = dollar_format()) +\n  labs(\n    title = \"Supplier A: Total Cost Curve with Tier Breakpoints\",\n    subtitle = \"Note the near-flat jumps at tier boundaries \u2014 the 4th and 7th units are bargains\",\n    x = \"Order Quantity (units)\",\n    y = \"Total Cost ($)\"\n  ) +\n  theme_qda\n\nggsave(\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_total_cost_curve.png\", p3,\n       width = 8, height = 5, dpi = 100, bg = \"white\")\n\n# =============================================================================\n# 6. CHART 4 \u2014 Supplier Comparison (Incremental Costs)\n# =============================================================================\n\ncomparison &lt;- bind_rows(sa, sb) %&gt;%\n  select(supplier, tier, qty_min, qty_max, price, incr_cost, roller_coaster)\n\ncomp_long &lt;- comparison %&gt;%\n  select(supplier, tier, incr_cost) %&gt;%\n  mutate(tier_label = case_when(\n    tier == 1 ~ \"Tier 1\\n(1\u20133 units)\",\n    tier == 2 ~ \"Tier 2\\n(4\u20136 units)\",\n    tier == 3 ~ \"Tier 3\\n(7\u201310 units)\"\n  ))\n\np4 &lt;- ggplot(comp_long, aes(x = tier_label, y = incr_cost, fill = supplier)) +\n  geom_col(position = position_dodge(width = 0.6), width = 0.5) +\n  geom_text(aes(label = dollar(incr_cost, accuracy = 0.01)),\n            position = position_dodge(width = 0.6),\n            vjust = -0.5, size = 3.5, fontface = \"bold\") +\n  # Add connecting lines to show trend\n  geom_line(aes(x = tier, y = incr_cost, color = supplier, group = supplier),\n            linewidth = 1, linetype = \"dashed\", show.legend = FALSE) +\n  geom_point(aes(x = tier, y = incr_cost, color = supplier),\n             size = 2.5, show.legend = FALSE) +\n  # Annotate roller-coaster for Supplier A\n  annotate(\"text\", x = 3.3, y = 70, label = \"\u2191 Roller-coaster\",\n           color = \"#e74c3c\", size = 3.5, fontface = \"bold\") +\n  # Annotate clean descent for Supplier B\n  annotate(\"text\", x = 3.3, y = 53, label = \"\u2193 Clean descent\",\n           color = \"#27ae60\", size = 3.5, fontface = \"bold\") +\n  scale_fill_manual(values = c(\"Supplier A\" = \"#e74c3c\", \"Supplier B\" = \"#27ae60\")) +\n  scale_color_manual(values = c(\"Supplier A\" = \"#e74c3c\", \"Supplier B\" = \"#27ae60\")) +\n  scale_y_continuous(labels = dollar_format(), limits = c(0, 95)) +\n  labs(\n    title = \"Supplier Comparison: Incremental Cost per Tier\",\n    subtitle = \"Supplier A's costs rise at Tier 3 (roller-coaster) while Supplier B's decline steadily\",\n    x = NULL,\n    y = \"Incremental Cost per Unit ($)\",\n    fill = NULL\n  ) +\n  theme_qda\n\nggsave(\"https:\/\/inphronesys.com\/wp-content\/uploads\/2026\/02\/qda_supplier_comparison.png\", p4,\n       width = 8, height = 5, dpi = 100, bg = \"white\")\n\n# =============================================================================\n# 7. SUMMARY OUTPUT\n# =============================================================================\n\ncat(\"\\n========================================\\n\")\ncat(\"QUANTITY DISCOUNT ANALYSIS SUMMARY\\n\")\ncat(\"========================================\\n\\n\")\n\ncat(\"SUPPLIER A:\\n\")\ncat(sprintf(\"  Tier 1: %d\u2013%d units @ $%.2f \u2192 Total $%.0f, Incremental $%.2f\/unit\\n\",\n            sa$qty_min[1], sa$qty_max[1], sa$price[1], sa$total_cost[1], sa$incr_cost[1]))\ncat(sprintf(\"  Tier 2: %d\u2013%d units @ $%.2f \u2192 Total $%.0f, Incremental $%.2f\/unit\\n\",\n            sa$qty_min[2], sa$qty_max[2], sa$price[2], sa$total_cost[2], sa$incr_cost[2]))\ncat(sprintf(\"  Tier 3: %d\u2013%d units @ $%.2f \u2192 Total $%.0f, Incremental $%.2f\/unit\\n\",\n            sa$qty_min[3], sa$qty_max[3], sa$price[3], sa$total_cost[3], sa$incr_cost[3]))\ncat(sprintf(\"  Roller-coaster detected: %s\\n\",\n            ifelse(any(sa$roller_coaster, na.rm = TRUE), \"YES\", \"NO\")))\n\ncat(\"\\nSUPPLIER B:\\n\")\ncat(sprintf(\"  Tier 1: %d\u2013%d units @ $%.2f \u2192 Total $%.0f, Incremental $%.2f\/unit\\n\",\n            sb$qty_min[1], sb$qty_max[1], sb$price[1], sb$total_cost[1], sb$incr_cost[1]))\ncat(sprintf(\"  Tier 2: %d\u2013%d units @ $%.2f \u2192 Total $%.0f, Incremental $%.2f\/unit\\n\",\n            sb$qty_min[2], sb$qty_max[2], sb$price[2], sb$total_cost[2], sb$incr_cost[2]))\ncat(sprintf(\"  Tier 3: %d\u2013%d units @ $%.2f \u2192 Total $%.0f, Incremental $%.2f\/unit\\n\",\n            sb$qty_min[3], sb$qty_max[3], sb$price[3], sb$total_cost[3], sb$incr_cost[3]))\ncat(sprintf(\"  Roller-coaster detected: %s\\n\",\n            ifelse(any(sb$roller_coaster, na.rm = TRUE), \"YES\", \"NO\")))\n\ncat(\"\\nRECOMMENDATION:\\n\")\ncat(\"  Orders 1\u20136 units: Supplier A (lower total cost)\\n\")\ncat(\"  Orders 7\u201310 units: Supplier B (lower total cost + clean incremental structure)\\n\")\n\n# =============================================================================\n# 8. APPLY TO YOUR OWN DATA\n# =============================================================================\n#\n# To analyze your own supplier quotes:\n#\n# 1. Create a discount schedule data frame:\n#\n#    my_supplier &lt;- data.frame(\n#      supplier = \"My Supplier\",\n#      tier     = 1:4,\n#      qty_min  = c(1, 50, 200, 500),\n#      qty_max  = c(49, 199, 499, 1000),\n#      price    = c(12.50, 11.00, 9.75, 9.25)\n#    )\n#\n# 2. Run the analysis:\n#\n#    result &lt;- compute_incremental_costs(my_supplier)\n#    print(result)\n#\n# 3. Check for roller-coaster:\n#\n#    if (any(result$roller_coaster, na.rm = TRUE)) {\n#      cat(\"WARNING: Roller-coaster effect detected!\\n\")\n#      cat(\"Tiers affected:\", result$tier[result$roller_coaster], \"\\n\")\n#    }\n#\n# 4. Generate charts by adapting the ggplot code above.\n<\/code><\/pre>\n<\/details>\n","protected":false},"excerpt":{"rendered":"<p>A supplier offers lower prices for larger orders \u2014 sounds great, right? Quantity Discount Analysis reveals that many discount schedules actually charge you more per incremental unit as volumes rise. Learn to spot this hidden trap with R.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[129,115],"tags":[22,133,53,132,130,119,131],"class_list":["post-1112","post","type-post","status-publish","format-standard","hentry","category-procurement","category-supply-chain-management","tag-cost-analysis","tag-incremental-cost","tag-procurement","tag-purchasing-analytics","tag-quantity-discount-analysis","tag-r-programming","tag-supplier-negotiation"],"_links":{"self":[{"href":"https:\/\/inphronesys.com\/index.php?rest_route=\/wp\/v2\/posts\/1112","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/inphronesys.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/inphronesys.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/inphronesys.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/inphronesys.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=1112"}],"version-history":[{"count":1,"href":"https:\/\/inphronesys.com\/index.php?rest_route=\/wp\/v2\/posts\/1112\/revisions"}],"predecessor-version":[{"id":1113,"href":"https:\/\/inphronesys.com\/index.php?rest_route=\/wp\/v2\/posts\/1112\/revisions\/1113"}],"wp:attachment":[{"href":"https:\/\/inphronesys.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1112"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/inphronesys.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1112"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/inphronesys.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1112"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}