← Python for Treasury Work

Lesson 1 · Beginner · 15 min

Add up a day's collections from a CSV file

Read the day's official receipts from a spreadsheet export and total them per fund, to the centavo.

✓ Last checked: November 2026 · code run, output real, facts checked

csvDecimaldictionariesf-strings

In this lesson

  • Read a CSV file one row at a time with the csv module
  • Keep peso amounts exact with Decimal
  • Group totals by fund using a dictionary
  • Print a neat, right-aligned summary with f-strings

At the end of each day a collector has a stack of official receipts, and the cashier needs one figure per fund: how much went to the General Fund, how much to the Special Education Fund, how much is held in trust. Most collection systems and spreadsheets can export those receipts as a CSV file. A few lines of Python can turn that file into the totals, the same way every time, without retyping a single amount.

The data

Save this as collections.csv. Each line is one official receipt. The first line holds the column names, which Python will use to find each value.

collections.csvDownload
or_number,date,payor,nature,fund,amount
7650101,2026-11-02,Santos Bakery,Business tax,General Fund,4500.00
7650102,2026-11-02,Juan Dela Cruz,Real property tax (basic),General Fund,1250.75
7650103,2026-11-02,Juan Dela Cruz,Real property tax (SEF),Special Education Fund,1250.75
7650104,2026-11-02,Maria Reyes,Community tax,General Fund,105.00
7650105,2026-11-02,Rivera Hardware,Mayor's permit fee,General Fund,1800.00
7650106,2026-11-02,Ana Lopez,Building permit fee,Trust Fund,2350.50
7650107,2026-11-02,Pedro Garcia,Market stall rental,General Fund,600.00
7650108,2026-11-02,Rosa Mendoza,Real property tax (basic),General Fund,980.40
7650109,2026-11-02,Rosa Mendoza,Real property tax (SEF),Special Education Fund,980.40
7650110,2026-11-02,Bautista Trading,Business tax,General Fund,12750.00

The code

Save this as daily_total.py in the same folder.

daily_total.pyDownload
"""Add up one day's collections from a CSV file, per fund and in total."""

import csv
from decimal import Decimal

totals = {}            # fund name -> running total
grand_total = Decimal("0")
receipts = 0

with open("collections.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        amount = Decimal(row["amount"])          # exact peso amount, never float
        fund = row["fund"]
        totals[fund] = totals.get(fund, Decimal("0")) + amount
        grand_total += amount
        receipts += 1

print(f"Collections for the day: {receipts} official receipts")
print("-" * 44)
for fund, total in sorted(totals.items()):
    print(f"{fund:<28}{total:>16,.2f}")
print("-" * 44)
print(f"{'TOTAL':<28}{grand_total:>16,.2f}")

Output

Output of daily_total.py
Collections for the day: 10 official receipts
--------------------------------------------
General Fund                       21,986.15
Special Education Fund              2,231.15
Trust Fund                          2,350.50
--------------------------------------------
TOTAL                              26,567.80

Line by line

If this is your first Python script, here is what each part does, in plain words.

"""Add up one day's collections..."""

Text between triple quotes at the top is a note for people. Python ignores it. Lines that start with # are notes too.

import csv

Brings in the csv module, Python's ready-made tool for reading CSV files. Without this line, Python would not know what csv.DictReader means.

from decimal import Decimal

Brings in just one tool, Decimal, from the decimal module, so we can write Decimal(...) directly.

totals = {}

Creates an empty dictionary named totals. A dictionary holds pairs, like a two-column table: the fund name on the left and its running total on the right.

grand_total = Decimal("0")

Creates a variable that starts at exactly zero pesos. receipts = 0 does the same for the count of receipts.

with open("collections.csv", ...) as f:

Opens the file and calls it f while we read it. The with keyword closes the file automatically when the indented block ends. The colon at the end means an indented block follows.

for row in csv.DictReader(f):

A loop. DictReader hands over the file one row at a time, skipping the header line, and the indented lines below run once for every row. With 10 receipts, they run 10 times.

amount = Decimal(row["amount"])

Row is a dictionary built from the header: row["amount"] is the text in the amount column, such as "4500.00". Decimal turns that text into an exact number we can add.

totals[fund] = totals.get(fund, Decimal("0")) + amount

Read it from the right: take this fund's total so far (or zero if the fund has not appeared yet), add this receipt's amount, and store the result back under the fund's name.

grand_total += amount

A short way of writing grand_total = grand_total + amount. receipts += 1 adds one to the count.

print(f"... {receipts} official receipts")

Prints a line of text. The f before the quote makes it an f-string: anything inside braces is replaced by its value, so {receipts} becomes 10.

print("-" * 44)

Repeating text: a dash times 44 prints a line of 44 dashes.

for fund, total in sorted(totals.items()):

Goes through the dictionary in alphabetical order. Each pass gives two values at once: the fund name and its total.

{fund:<28} and {total:>16,.2f}

Formatting codes inside the braces. <28 means left-aligned in 28 spaces. >16 means right-aligned in 16 spaces, the comma adds thousands separators, and .2f shows two decimal places. That is what makes the columns line up.

Note: Open a CSV file with newline="" and an explicit encoding, as Python's own documentation advises. newline="" lets the csv module handle line breaks itself (and, when you write a CSV, it prevents blank rows on Windows); encoding="utf-8" keeps names with ñ readable on every computer.

Try it yourself

Practice

Add a second total per nature of collection (Business tax, Real property tax, and so on). Hint: make another dictionary and fill it inside the same loop.

Show answer
answer_by_nature.pyDownload
"""Answer: totals per fund AND per nature of collection, in one pass over the file."""

import csv
from decimal import Decimal

by_fund = {}
by_nature = {}          # the second dictionary

with open("collections.csv", newline="", encoding="utf-8") as f:
    for row in csv.DictReader(f):
        amount = Decimal(row["amount"])
        by_fund[row["fund"]] = by_fund.get(row["fund"], Decimal("0")) + amount
        by_nature[row["nature"]] = by_nature.get(row["nature"], Decimal("0")) + amount

print("By fund")
for fund, total in sorted(by_fund.items()):
    print(f"  {fund:<30}{total:>12,.2f}")

print("\nBy nature of collection")
for nature, total in sorted(by_nature.items()):
    print(f"  {nature:<30}{total:>12,.2f}")
print(f"  {'TOTAL':<30}{sum(by_nature.values()):>12,.2f}")
Output of answer_by_nature.py
By fund
  General Fund                     21,986.15
  Special Education Fund            2,231.15
  Trust Fund                        2,350.50

By nature of collection
  Building permit fee               2,350.50
  Business tax                     17,250.00
  Community tax                       105.00
  Market stall rental                 600.00
  Mayor's permit fee                1,800.00
  Real property tax (SEF)           2,231.15
  Real property tax (basic)         2,231.15
  TOTAL                            26,567.80
← Lesson 0Lesson 2 comes out November 14, 2026