Data AnalyticsIndustry analytics playbooks

FMCG beat productivity: bills cut, lines per call and secondary sales

PK
Pankit Kumar
Sr. Data Scientist at Parexel (a Goldman Sachs–backed company) · 27 September 2026 · 5 min read
In this article (7 sections)

Related programme: the Data Analytics with Generative AI course (3–4 months; ₹90,000 (inclusive of GST) · EMI from ₹4,000/month).

The short answer

Beat productivity is productive calls (outlets that placed an order) divided by outlets scheduled on the beat, and lines per call is the number of distinct SKUs billed per productive call. Together with average value per call, they explain where a salesman's secondary sales come from: reaching more outlets, selling more of the range into each one, or selling bigger quantities of a few packs. Build them from the beat plan and the distributor's invoice lines, and count an outlet billed twice on one day as one call.

Two salesmen, one number that hides the difference

An area sales manager (ASM) looking only at weekly secondary sales sees Ravi at ₹9,250 and Imran at ₹4,600 and concludes that Ravi is the better salesman. The scorecard below shows a different picture: Imran converts more of his beat, while Ravi sells a wider range into fewer outlets. They need different coaching, and the value figure alone cannot tell you which.

Where the data comes from

Beat plan (from the sales force app or a planning sheet): salesman, beat_id, visit_date, outlets_scheduled, and ideally one row per scheduled outlet.

Distributor invoice lines (from the distributor management system, DMS): bill_no, bill_date, salesman, outlet_id, sku, qty, gross_value, scheme_discount, net_value.

This is secondary sales — distributor to retailer. Primary sales (company to distributor) are a different table and should never be mixed into a beat scorecard; a distributor stocking up before a price increase says nothing about outlet demand.

Metric definitions

MetricFormula
Productive callsDistinct (outlet, date) pairs with at least one line of positive net value
Beat productivity %Productive calls ÷ outlets scheduled × 100
Lines per callΣ distinct SKUs per productive call ÷ productive calls
Average value per callNet secondary value ÷ productive calls
Secondary valueΣ net_value after scheme discounts, before returns adjustment

Scorecard code (synthetic week, scaled down)

The beats below are shortened to two or three outlets a day so the example stays readable.

python
import pandas as pd

# Synthetic: one week of beat plans and distributor (DMS) invoice lines
plan = pd.DataFrame({
    "salesman": ["Ravi"]*3 + ["Imran"]*3,
    "visit_date": ["2026-09-21","2026-09-22","2026-09-23"]*2,
    "outlets_scheduled": [3, 2, 3, 3, 3, 3],
})
lines = pd.DataFrame({
    "salesman": ["Ravi"]*9 + ["Imran"]*9,
    "visit_date": ["2026-09-21"]*4 + ["2026-09-22"]*3 + ["2026-09-23"]*2
                + ["2026-09-21"]*3 + ["2026-09-22"]*3 + ["2026-09-23"]*3,
    "outlet_id": ["O1","O1","O2","O2","O3","O3","O4","O5","O5",
                  "K1","K2","K3","K4","K4","K5","K6","K7","K7"],
    "bill_no":   ["B1","B1","B2","B3","B4","B4","B5","B6","B6",
                  "C1","C2","C3","C4","C4b","C5","C6","C7","C8"],
    "sku": ["Soap75","Paste100","Soap75","Shampoo","Soap75","Paste100","Soap75","Paste100","Shampoo",
            "Soap75","Soap75","Soap75","Soap75","Paste100","Soap75","Soap75","Soap75","FreeSachet"],
    "net_value": [1200, 800, 900, 1500, 700, 650, 1000, 1400, 1100,
                  600, 550, 500, 650, 400, 700, 580, 620, 0],
})
lines = lines[lines["net_value"] > 0]          # scheme freebies are not selling lines

# A productive call = an outlet billed on a day, however many invoices it produced
calls = (lines.groupby(["salesman","visit_date","outlet_id"])
              .agg(sku_lines=("sku","nunique"), value=("net_value","sum")).reset_index())

scorecard = (calls.groupby("salesman")
                  .agg(productive_calls=("outlet_id","size"),
                       lines=("sku_lines","sum"), value=("value","sum"))
             .join(plan.groupby("salesman")["outlets_scheduled"].sum()))
scorecard["productivity_pct"] = (100 * scorecard["productive_calls"] / scorecard["outlets_scheduled"]).round(1)
scorecard["lines_per_call"] = (scorecard["lines"] / scorecard["productive_calls"]).round(2)
scorecard["avg_value_per_call"] = (scorecard["value"] / scorecard["productive_calls"]).round(0)
print(scorecard[["outlets_scheduled","productive_calls","productivity_pct","lines_per_call","avg_value_per_call","value"]].to_string())

naive = lines.groupby("salesman")["bill_no"].nunique()
print("\nInvoices counted as calls (the wrong way):", naive.to_dict())
text
          outlets_scheduled  productive_calls  productivity_pct  lines_per_call  avg_value_per_call  value
salesman
Imran                     9                 7              77.8            1.14               657.0   4600
Ravi                      8                 5              62.5            1.80              1850.0   9250

Invoices counted as calls (the wrong way): {'Imran': 8, 'Ravi': 6}

Imran billed 78% of his scheduled outlets but almost always a single SKU — 1.14 lines per call, and Soap75 in every one of his calls. Ravi billed only 62.5% of his outlets but sold close to two SKUs per call at nearly three times the value.

What each pattern tells the ASM to do

PatternLikely readingCoaching action
High productivity, low lines per callReaches outlets, sells only the "must-stock" packRange-selling targets; a focus-SKU list per beat
Low productivity, high lines per callStrong with a core set of outlets, skipping or failing at the restCheck visit compliance (GPS/app check-ins) before judging selling skill
Both lowBeat may be badly designed, or the salesman is strugglingRide along for a day before acting
Both high, low value per callSelling small packs into small outletsMay be correct for that beat's outlet mix; compare with outlet class

Mistakes that distort the scorecard

Counting invoices as calls. Imran's outlet K4 received two bills on 22 September. Counting invoices gives Imran 8 calls and Ravi 6, and overstates productivity. Use distinct outlet-date pairs.

Free-goods lines. The zero-value FreeSachet line is a scheme freebie. Left in, it would raise lines per call for a salesman who sold nothing extra. The code drops lines with non-positive net value; adjust if your DMS records schemes differently.

Returns and credit notes. Expiry returns taken back on the next visit arrive as negative lines or separate credit notes. Net them in the value metric for the month, but do not let a return day make a productive call look unproductive.

Unplanned outlets. A salesman who bills an outlet not on the day's beat plan raises productive calls without raising the denominator. Decide whether off-beat calls count and apply it to everyone.

SKU codes that are the same product. A soap sold in a four-pack and singly may carry two SKU codes. Lines per call then rewards pack variety, not range. Map to a product family if range selling is the intent.

Putting it in front of the ASM

ASMs read scorecards on phones between market visits. One row per salesman, the four metrics, and one arrow per metric against the previous four weeks is enough. Put secondary value last, not first, so the conversation starts with how the value was earned. Pair each salesman with the matching row of the coaching table rather than a ranking; a league table of value per salesman rewards whoever has the richest beat.

Scorecards like this are standard exercises in the Data Analyst programme. For people already in sales roles, sales executive to sales analyst describes how to turn beat data you already handle into a portfolio project.

Frequently asked questions

What is beat productivity in FMCG sales?

It is the share of outlets scheduled on a salesman's beat that actually placed an order that day — productive calls divided by scheduled calls. Some companies divide by outlets visited instead, so always state the denominator.

What does lines per call mean?

Lines per call is the number of distinct SKUs billed per productive call. It shows range selling — whether the salesman sells only the fast-moving pack or gets the outlet to stock more of the portfolio.

PK
Pankit Kumar
Lead Instructor, NeuraPath Academy

Pankit Kumar has 10 years in Data Science & AI, building and shipping production systems in regulated pharma and clinical environments. He is a freelance trainer at Boston Institute of Analytics, AnalytixLabs and Scaler, and has taught this material to thousands of working professionals.

This article is part of our Data Analytics with Generative AI programme — 3–4 months. The full analyst stack — Excel, SQL, Power BI and Python pipelines — then a generative-AI layer you can prove is right.

Explore Data Analytics with Generative AI
Counselling is free · no obligation

Not sure which programme fits?

Tell us your background and we will map it to the right entry point — including saying so when a cheaper programme is the better fit. A counsellor replies within one working day.