<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://mediawiki.comfac.net/index.php?action=history&amp;feed=atom&amp;title=Webshop-14-Hypothesis-Discount-Deadline-Visibility</id>
	<title>Webshop-14-Hypothesis-Discount-Deadline-Visibility - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://mediawiki.comfac.net/index.php?action=history&amp;feed=atom&amp;title=Webshop-14-Hypothesis-Discount-Deadline-Visibility"/>
	<link rel="alternate" type="text/html" href="https://mediawiki.comfac.net/index.php?title=Webshop-14-Hypothesis-Discount-Deadline-Visibility&amp;action=history"/>
	<updated>2026-06-05T11:03:57Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.45.1</generator>
	<entry>
		<id>https://mediawiki.comfac.net/index.php?title=Webshop-14-Hypothesis-Discount-Deadline-Visibility&amp;diff=172&amp;oldid=prev</id>
		<title>Justinaquino: &quot;Add all 14 Frappe ERPNext Webshop chapter pages from wikitext-upload&quot;</title>
		<link rel="alternate" type="text/html" href="https://mediawiki.comfac.net/index.php?title=Webshop-14-Hypothesis-Discount-Deadline-Visibility&amp;diff=172&amp;oldid=prev"/>
		<updated>2026-03-06T13:00:43Z</updated>

		<summary type="html">&lt;p&gt;&amp;quot;Add all 14 Frappe ERPNext Webshop chapter pages from wikitext-upload&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;= 14 - Hypothesis: Making Discounts and Deadlines Visible =&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Parent:&amp;#039;&amp;#039;&amp;#039; [Webshop-Index Webshop Index]  &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Previous:&amp;#039;&amp;#039;&amp;#039; [Webshop-13-Discount-Visibility-and-Urgency 13 - Discount Visibility &amp;amp; Urgency]  &lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Source:&amp;#039;&amp;#039;&amp;#039; [https://github.com/Comfac-Global-Group/comfac-webshop/wiki/14-Hypothesis-Discount-Deadline-Visibility Comfac Webshop Wiki - Chapter 14]&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
== Prerequisite: Seamless Baseline First ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;This hypothesis is NOT to be executed until the comfac-webshop fork runs seamlessly as a drop-in replacement on a production-like ERPNext instance.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Execution order:&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
1. &amp;#039;&amp;#039;&amp;#039;Phase 0 (Staging Sandbox):&amp;#039;&amp;#039;&amp;#039; Clone the production ERPNext instance, install comfac-webshop, validate it works identically to the existing webshop. No feature changes. Pass all validation checks.&lt;br /&gt;
&lt;br /&gt;
2. &amp;#039;&amp;#039;&amp;#039;Phase 1 (Experiment Clone):&amp;#039;&amp;#039;&amp;#039; Once Phase 0 is proven stable, clone THAT successful staging instance into a second sandbox. This is where we implement and test the hypothesis below.&lt;br /&gt;
&lt;br /&gt;
3. &amp;#039;&amp;#039;&amp;#039;Phase 2 (Iterate):&amp;#039;&amp;#039;&amp;#039; Test with sample products, pricing rules, coupons, and deadline-bearing offers on the experiment clone. Validate every scenario. Only when everything passes do we merge back to the main fork.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Do not skip Phase 0.&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
== Core Thesis ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;All the data we need already exists on the Quotation document.&amp;#039;&amp;#039;&amp;#039; ERPNext&amp;#039;s pricing engine populates discount fields on every cart save. We do not need to change the backend calculation logic. We need to:&lt;br /&gt;
&lt;br /&gt;
1. &amp;#039;&amp;#039;&amp;#039;Surface existing hidden fields&amp;#039;&amp;#039;&amp;#039; in the cart templates&lt;br /&gt;
2. &amp;#039;&amp;#039;&amp;#039;Enrich the decorator&amp;#039;&amp;#039;&amp;#039; to pass pricing rule metadata (deadlines, titles) to the frontend&lt;br /&gt;
3. &amp;#039;&amp;#039;&amp;#039;Add summation logic&amp;#039;&amp;#039;&amp;#039; in the payment summary template&lt;br /&gt;
4. &amp;#039;&amp;#039;&amp;#039;Style it&amp;#039;&amp;#039;&amp;#039; so customers immediately see value and urgency&lt;br /&gt;
&lt;br /&gt;
== Part 1: The Fields We Will Target ==&lt;br /&gt;
&lt;br /&gt;
=== Per-Item Fields (Quotation Item - already populated) ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| What It Contains&lt;br /&gt;
| Currently Used?&lt;br /&gt;
|}&lt;br /&gt;
|---------|--------|------------------------|----------------------|&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;price_list_rate&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Original unit price from the Price List (before any discount)&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;rate&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Final unit price (after discount applied)&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;discount_percentage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Percent&lt;br /&gt;
| The % discount applied by the Pricing Rule&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;discount_amount&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Per-unit discount in currency&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;amount&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| &amp;lt;code&amp;gt;rate * qty&amp;lt;/code&amp;gt; = line total after discount&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;is_free_item&amp;lt;/code&amp;gt;&lt;br /&gt;
| Check&lt;br /&gt;
| True if item was added by a &amp;quot;Free Item&amp;quot; pricing rule&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pricing_rules&amp;lt;/code&amp;gt;&lt;br /&gt;
| Small Text&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;JSON string&amp;#039;&amp;#039;&amp;#039; of Pricing Rule document names&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Document-Level Fields (Quotation - already populated) ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| What It Contains&lt;br /&gt;
| Currently Used?&lt;br /&gt;
|}&lt;br /&gt;
|---------|--------|------------------------|----------------------|&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;total&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Sum of all &amp;lt;code&amp;gt;amount&amp;lt;/code&amp;gt; values (item totals before tax)&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;net_total&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Total after additional discounts&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;grand_total&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Final total with taxes&lt;br /&gt;
| YES&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;discount_amount&amp;lt;/code&amp;gt;&lt;br /&gt;
| Currency&lt;br /&gt;
| Additional discount on the overall total&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;additional_discount_percentage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Percent&lt;br /&gt;
| % discount on total&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;NO&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;coupon_code&amp;lt;/code&amp;gt;&lt;br /&gt;
| Link&lt;br /&gt;
| Applied Coupon Code document name&lt;br /&gt;
| PARTIAL&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== Pricing Rule Fields (need to be looked up from pricing_rules JSON) ===&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Field&lt;br /&gt;
| Type&lt;br /&gt;
| What It Contains&lt;br /&gt;
| Purpose for Us&lt;br /&gt;
|}&lt;br /&gt;
|---------|--------|------------------------|---------------------|&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;title&amp;lt;/code&amp;gt;&lt;br /&gt;
| Data&lt;br /&gt;
| Human-readable name (e.g., &amp;quot;Spring GPU Sale&amp;quot;)&lt;br /&gt;
| Display offer name&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;valid_from&amp;lt;/code&amp;gt;&lt;br /&gt;
| Date&lt;br /&gt;
| Start date of the offer&lt;br /&gt;
| Show when offer started&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;valid_upto&amp;lt;/code&amp;gt;&lt;br /&gt;
| Date&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;End date / DEADLINE&amp;#039;&amp;#039;&amp;#039; of the offer&lt;br /&gt;
| &amp;#039;&amp;#039;&amp;#039;URGENCY DISPLAY&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;discount_percentage&amp;lt;/code&amp;gt;&lt;br /&gt;
| Percent&lt;br /&gt;
| The discount % this rule applies&lt;br /&gt;
| Confirm which rule gave what discount&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Part 2: The Summation System ==&lt;br /&gt;
&lt;br /&gt;
=== Improved Summation Formula ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python&lt;br /&gt;
Per-item savings:&lt;br /&gt;
    if item.is_free_item:&lt;br /&gt;
        item_savings = item.price_list_rate * item.qty  (entire value is free)&lt;br /&gt;
    elif item.price_list_rate and item.price_list_rate &amp;gt; item.rate:&lt;br /&gt;
        item_savings = (item.price_list_rate - item.rate) * item.qty&lt;br /&gt;
    else:&lt;br /&gt;
        item_savings = 0&lt;br /&gt;
&lt;br /&gt;
total_item_savings = sum(item_savings for all items)&lt;br /&gt;
&lt;br /&gt;
Additional discount savings:&lt;br /&gt;
    doc_discount = doc.discount_amount or 0&lt;br /&gt;
&lt;br /&gt;
Grand savings:&lt;br /&gt;
    total_savings = total_item_savings + doc_discount&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 3: The Deadline System ==&lt;br /&gt;
&lt;br /&gt;
=== Data Chain for Deadlines ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Pricing Rule (has valid_upto date)&lt;br /&gt;
    ↓ applied during set_price_list_and_item_details()&lt;br /&gt;
Quotation Item.pricing_rules = &amp;#039;[&amp;quot;PRLE-0001&amp;quot;, &amp;quot;PRLE-0002&amp;quot;]&amp;#039;  (JSON string)&lt;br /&gt;
    ↓ we parse this in the decorator&lt;br /&gt;
decorate_quotation_doc() enriches each item with:&lt;br /&gt;
    d.offer_details = [&lt;br /&gt;
        {&amp;quot;title&amp;quot;: &amp;quot;Spring Sale&amp;quot;, &amp;quot;valid_upto&amp;quot;: &amp;quot;2026-03-01&amp;quot;, &amp;quot;days_left&amp;quot;: 10},&lt;br /&gt;
        ...&lt;br /&gt;
    ]&lt;br /&gt;
    ↓ template renders deadline badges&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Backend Helper ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;python&lt;br /&gt;
def get_offer_details_for_item(item):&lt;br /&gt;
    &amp;quot;&amp;quot;&amp;quot;Parse pricing_rules JSON and fetch deadline/title from each Pricing Rule.&amp;quot;&amp;quot;&amp;quot;&lt;br /&gt;
    import json&lt;br /&gt;
    offers = []&lt;br /&gt;
    &lt;br /&gt;
    if not item.pricing_rules:&lt;br /&gt;
        return offers&lt;br /&gt;
    &lt;br /&gt;
    try:&lt;br /&gt;
        rule_names = json.loads(item.pricing_rules)&lt;br /&gt;
    except (json.JSONDecodeError, TypeError):&lt;br /&gt;
        return offers&lt;br /&gt;
    &lt;br /&gt;
    for rule_name in rule_names:&lt;br /&gt;
        rule = frappe.get_cached_doc(&amp;quot;Pricing Rule&amp;quot;, rule_name)&lt;br /&gt;
        offer = {&lt;br /&gt;
            &amp;quot;title&amp;quot;: rule.title or rule.name,&lt;br /&gt;
            &amp;quot;valid_upto&amp;quot;: rule.valid_upto,&lt;br /&gt;
            &amp;quot;days_left&amp;quot;: None,&lt;br /&gt;
        }&lt;br /&gt;
        if rule.valid_upto:&lt;br /&gt;
            from frappe.utils import date_diff, today&lt;br /&gt;
            offer[&amp;quot;days_left&amp;quot;] = date_diff(rule.valid_upto, today())&lt;br /&gt;
        offers.append(offer)&lt;br /&gt;
    &lt;br /&gt;
    return offers&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Part 4: File-by-File Change Map ==&lt;br /&gt;
&lt;br /&gt;
=== File 1: &amp;lt;code&amp;gt;webshop/webshop/shopping_cart/cart.py&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Function:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;decorate_quotation_doc()&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Change:&amp;#039;&amp;#039;&amp;#039; Add &amp;lt;code&amp;gt;offer_details&amp;lt;/code&amp;gt; list to each item. Also compute and add &amp;lt;code&amp;gt;doc.original_total&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;doc.total_savings&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;doc.has_any_discount&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== File 2: &amp;lt;code&amp;gt;templates/includes/cart/cart_items.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Macro:&amp;#039;&amp;#039;&amp;#039; &amp;lt;code&amp;gt;item_subtotal&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Change:&amp;#039;&amp;#039;&amp;#039; Add strike-through original price when discounted, discount percentage badge, &amp;quot;You save&amp;quot; amount, and offer deadline badges.&lt;br /&gt;
&lt;br /&gt;
=== File 3: &amp;lt;code&amp;gt;templates/includes/cart/cart_payment_summary.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Change:&amp;#039;&amp;#039;&amp;#039; Add original subtotal (struck through) if discounts exist, savings row, and additional discount row.&lt;br /&gt;
&lt;br /&gt;
=== File 4: &amp;lt;code&amp;gt;templates/includes/cart/cart_items_total.html&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Change:&amp;#039;&amp;#039;&amp;#039; Show original total (struck through) if &amp;lt;code&amp;gt;doc.has_any_discount&amp;lt;/code&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== File 5: &amp;lt;code&amp;gt;public/scss/webshop_cart.scss&amp;lt;/code&amp;gt; ===&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Change:&amp;#039;&amp;#039;&amp;#039; Add styles for discount badges, strikethrough, and urgency indicators.&lt;br /&gt;
&lt;br /&gt;
== Part 5: What We Do NOT Need to Change ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Component&lt;br /&gt;
| Why No Change Needed&lt;br /&gt;
|}&lt;br /&gt;
|--------------|----------------------------|&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;set_price_list_and_rate()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Already resets and repopulates fields on every save&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;apply_cart_settings()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Already calls &amp;lt;code&amp;gt;calculate_taxes_and_totals()&amp;lt;/code&amp;gt; which does all the math&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;update_cart()&amp;lt;/code&amp;gt;&lt;br /&gt;
| Already re-renders all three template fragments on AJAX update&lt;br /&gt;
|-&lt;br /&gt;
| ERPNext Pricing Rules&lt;br /&gt;
| Standard feature - we just read the results&lt;br /&gt;
|-&lt;br /&gt;
| ERPNext Quotation DocType&lt;br /&gt;
| All fields we need already exist&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;shopping_cart.js&amp;lt;/code&amp;gt;&lt;br /&gt;
| AJAX update already replaces affected DOM elements&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Part 6: Risk Assessment ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| Risk&lt;br /&gt;
| Likelihood&lt;br /&gt;
| Mitigation&lt;br /&gt;
|}&lt;br /&gt;
|--------|----------------|----------------|&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;price_list_rate&amp;lt;/code&amp;gt; is 0 or null&lt;br /&gt;
| Medium&lt;br /&gt;
| Guard with &amp;lt;code&amp;gt;{% if item.price_list_rate and item.price_list_rate &amp;gt; 0 %}&amp;lt;/code&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;pricing_rules&amp;lt;/code&amp;gt; JSON malformed&lt;br /&gt;
| Low&lt;br /&gt;
| Wrap in try/except, return empty list&lt;br /&gt;
|-&lt;br /&gt;
| Pricing Rule deleted but referenced&lt;br /&gt;
| Low&lt;br /&gt;
| Use &amp;lt;code&amp;gt;frappe.db.exists()&amp;lt;/code&amp;gt; check&lt;br /&gt;
|-&lt;br /&gt;
| Performance hit from lookups&lt;br /&gt;
| Low&lt;br /&gt;
| &amp;lt;code&amp;gt;get_cached_doc()&amp;lt;/code&amp;gt; uses Frappe cache&lt;br /&gt;
|-&lt;br /&gt;
| &amp;lt;code&amp;gt;discount_percentage&amp;lt;/code&amp;gt; is 0 but &amp;lt;code&amp;gt;discount_amount&amp;lt;/code&amp;gt; is set&lt;br /&gt;
| Medium&lt;br /&gt;
| Use &amp;lt;code&amp;gt;price_list_rate - rate&amp;lt;/code&amp;gt; comparison&lt;br /&gt;
|-&lt;br /&gt;
| Free items with no &amp;lt;code&amp;gt;price_list_rate&amp;lt;/code&amp;gt;&lt;br /&gt;
| Medium&lt;br /&gt;
| Skip savings calculation for these&lt;br /&gt;
|-&lt;br /&gt;
| Multiple pricing rules per item&lt;br /&gt;
| Medium&lt;br /&gt;
| Show all offer details; sum net savings&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Part 7: Verification Checklist ==&lt;br /&gt;
&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| #&lt;br /&gt;
| Scenario&lt;br /&gt;
| Check&lt;br /&gt;
|}&lt;br /&gt;
|----|-------------|---------|&lt;br /&gt;
{| class=&amp;quot;wikitable&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 1&lt;br /&gt;
| Item with percentage discount&lt;br /&gt;
| Shows $100 → $80 (-20%), &amp;quot;You save $20&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 2&lt;br /&gt;
| Item with amount discount&lt;br /&gt;
| Shows $100 → $85, &amp;quot;You save $15&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 3&lt;br /&gt;
| Item with no discount&lt;br /&gt;
| Shows $100 only, no strikethrough&lt;br /&gt;
|-&lt;br /&gt;
| 4&lt;br /&gt;
| Free item from pricing rule&lt;br /&gt;
| Shows &amp;quot;FREE&amp;quot; badge, value in savings&lt;br /&gt;
|-&lt;br /&gt;
| 5&lt;br /&gt;
| Coupon code applied&lt;br /&gt;
| Savings row reflects coupon&lt;br /&gt;
|-&lt;br /&gt;
| 6&lt;br /&gt;
| Offer expiring in 3 days&lt;br /&gt;
| Red text &amp;quot;Only 3 days left!&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 7&lt;br /&gt;
| Offer expiring in 10 days&lt;br /&gt;
| Warning text &amp;quot;Offer ends March 1, 2026&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
| 8&lt;br /&gt;
| Offer with no deadline&lt;br /&gt;
| No deadline shown, discount visible&lt;br /&gt;
|-&lt;br /&gt;
| 9&lt;br /&gt;
| Multiple offers on one item&lt;br /&gt;
| All offer titles/deadlines listed&lt;br /&gt;
|-&lt;br /&gt;
| 10&lt;br /&gt;
| Additional discount on total&lt;br /&gt;
| &amp;quot;Additional Discount -$50&amp;quot; row&lt;br /&gt;
|-&lt;br /&gt;
| 11&lt;br /&gt;
| Cart with mixed items&lt;br /&gt;
| Only discounted items show strikethrough&lt;br /&gt;
|-&lt;br /&gt;
| 12&lt;br /&gt;
| AJAX qty update&lt;br /&gt;
| All discount info re-renders&lt;br /&gt;
|-&lt;br /&gt;
| 13&lt;br /&gt;
| Mobile view&lt;br /&gt;
| Discount badges readable&lt;br /&gt;
|-&lt;br /&gt;
| 14&lt;br /&gt;
| &amp;lt;code&amp;gt;price_list_rate&amp;lt;/code&amp;gt; is null&lt;br /&gt;
| Falls back showing rate only&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Summary ==&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What we&amp;#039;re doing:&amp;#039;&amp;#039;&amp;#039; Reading 4 hidden Quotation Item fields (&amp;lt;code&amp;gt;price_list_rate&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;discount_percentage&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;pricing_rules&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;is_free_item&amp;lt;/code&amp;gt;) + 2 hidden Quotation fields (&amp;lt;code&amp;gt;discount_amount&amp;lt;/code&amp;gt;, &amp;lt;code&amp;gt;additional_discount_percentage&amp;lt;/code&amp;gt;) that ERPNext already populates, and displaying them in 4 template files + 1 Python decorator.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;What we&amp;#039;re NOT doing:&amp;#039;&amp;#039;&amp;#039; No new DocTypes, no pricing engine changes, no database migrations, no JS logic changes. The AJAX update cycle already re-renders all affected templates.&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Files touched:&amp;#039;&amp;#039;&amp;#039; 5 total (1 Python, 3 HTML templates, 1 SCSS)&lt;br /&gt;
&lt;br /&gt;
----&lt;br /&gt;
&lt;br /&gt;
&amp;#039;&amp;#039;&amp;#039;Navigation:&amp;#039;&amp;#039;&amp;#039; [Webshop-Index Webshop Index] | [Webshop-13-Discount-Visibility-and-Urgency Previous: 13 - Discount Visibility] | [Webshop-Index Webshop Index]&lt;/div&gt;</summary>
		<author><name>Justinaquino</name></author>
	</entry>
</feed>