Generate a branch-wise daily sales summary for WhatsApp
In this article (6 sections)
Related programme: the Data Analytics with Generative AI course (3–4 months; ₹90,000 (inclusive of GST) · EMI from ₹4,000/month).
A good day-end sales message fits on one phone screen: the total with Indian digit grouping, month-to-date against target alongside how much of the month has passed, each branch ranked by performance against its day target, and a line naming any branch that has not reported. Generate it from the same table that feeds your dashboard, so the message and the report can never disagree, and mark the figures as provisional until the books are closed.
Why the message deserves a script
In many Indian sales teams, the day-end WhatsApp message is read more than any dashboard. It is often typed by hand at 9 pm from a spreadsheet, which is exactly when mistakes happen: a branch copied twice, a lakh typed as ten lakh, yesterday's number left in. A script that builds the text from the data removes the typing and leaves only the checking.
Decide what goes in before writing code
Ask the regional manager what they do with the message. The usual answers point to five things, in this order:
- 1How much did we sell today, and is everyone included?
- 2Are we on course for the month?
- 3Which branches did well or badly against their targets?
- 4Who still has to report?
- 5Can I trust these numbers yet?
Anything else, such as product mix or conversion rates, belongs in the full report. Every extra line pushes the ranking below the fold of a phone screen.
Composing the message from the day's rows
The branches, sales and targets below are synthetic.
from datetime import date
import pandas as pd
def inr(v: float) -> str:
"""Whole rupees with Indian grouping: 1234567 -> '12,34,567'."""
s = str(int(round(abs(v))))
head, tail = s[:-3], s[-3:]
parts = []
while len(head) > 2:
parts.insert(0, head[-2:]); head = head[:-2]
return ("-" if v < 0 else "") + ",".join(([head] if head else []) + parts + [tail])
def short(v: float) -> str:
return f"{v / 1e7:.2f} Cr" if abs(v) >= 1e7 else f"{v / 1e5:.2f} L"
# Synthetic day-end data for 26 Sep 2026 (a Saturday).
day = date(2026, 9, 26)
sales = pd.DataFrame({
"branch": ["Noida Sec 18", "Noida Sec 62", "Ghaziabad", "Gurugram", "Faridabad", "Greater Noida"],
"today": [412500, 298000, 187250, 356900, 0, 144300],
"target_day": [380000, 320000, 200000, 350000, 150000, 160000],
"mtd": [8120000, 6390000, 4125000, 7482000, 2890000, 3010500],
"target_month": [11000000, 9500000, 6000000, 10500000, 4500000, 4800000],
"reported": [True, True, True, True, False, True],
})
rep = sales[sales["reported"]].copy()
rep["vs_target"] = rep["today"] / rep["target_day"] - 1
total_today = rep["today"].sum()
mtd = sales["mtd"].sum()
target_m = sales["target_month"].sum()
lines = [f"*Day-end sales | {day:%a %d %b %Y}*", ""]
lines.append(f"Total today: ₹{inr(total_today)} ({len(rep)}/{len(sales)} branches reported)")
days_in_month = pd.Timestamp(day).days_in_month
pace = day.day / days_in_month # share of the month elapsed
lines.append(f"Month to date: ₹{short(mtd)} of ₹{short(target_m)} target "
f"({mtd / target_m:.0%} achieved, {pace:.0%} of month gone)")
lines.append("")
for r in rep.sort_values("vs_target", ascending=False).itertuples():
mark = "above" if r.vs_target >= 0 else "below"
lines.append(f"- {r.branch}: ₹{inr(r.today)} ({abs(r.vs_target):.0%} {mark} day target)")
missing = sales.loc[~sales["reported"], "branch"].tolist()
if missing:
lines += ["", "Not yet reported: " + ", ".join(missing)]
lines += ["", "_Figures are provisional until the day book is closed._"]
message = "\n".join(lines)
print(message)
with open(f"sales_{day:%Y%m%d}.txt", "w", encoding="utf-8") as f:
f.write(message)
assert total_today == 1398950 # control: matches the sum of reported branchesThe output is plain text ready to paste. WhatsApp shows text between asterisks in bold and text between underscores in italics, which is why the title and the footnote are wrapped that way; on email or SMS, those characters simply show as typed, which is harmless.
Three choices that make the message trustworthy
Pace next to achievement. "69% of target" means nothing without the date. Printing "87% of month gone" beside it tells the reader at once that the month is behind. It is a crude measure, since weekends and festival days do not sell evenly, but it is honest and far better than a bare percentage. If your business has strongly uneven days, replace the calendar share with the share of the month's budget that was phased up to today.
Branches ranked against their own target. Ranking by rupee sales would put the biggest branch first every day and bury a small branch that beat its target comfortably. Ranking by percentage against the day target is fairer, and the rupee figure is still there for context.
The missing branch is named, not zeroed. Faridabad has not sent its figures. Showing it as ₹0 at the bottom of the ranking would start a pointless argument; naming it as "not yet reported" and counting 5/6 branches in the total line is accurate. The total today covers only reported branches, and the assert at the end checks that it equals their sum.
Formatting numbers the way readers expect
The inr helper prints ₹13,98,950 with Indian grouping, and short prints ₹3.20 Cr for large totals. Write these as small functions once and reuse them; f"{n:,}" would produce 1,398,950, which looks wrong to most Indian business readers. A tested version that handles decimals is in show numbers in lakhs and crores in Excel, Power BI and Python.
Keep percentages to whole numbers in the message. Nobody on a phone needs 8.55%, and one decimal place across six branches makes the list hard to scan.
From a script to a routine
Once the message looks right, make it part of the day close:
- Read branch figures from the same source as the dashboard, whether that is a shared sheet, an ERP export or a database view.
- Save each day's message as a dated text file, as the script does, so there is a record of what was sent.
- Run the script, read the message once, then paste it. The human check stays; only the typing goes.
If the same data also needs to go out as an email with an attached workbook, email an Excel report from Python with smtplib covers that step. Turning raw sales data into short, correct communication is a large part of analyst work in retail and distribution, and it is practised throughout our Data Analyst programme.
Frequently asked questions
Can Python send the WhatsApp message automatically?
Sending to groups from a personal account is not something to automate with unofficial tools. Generate the text automatically, and either paste it yourself or send it through an approved business messaging provider your company already uses.
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