Inventory Management Software

Kardex Tauro

Kardex Tauro® Inventory Software is designed to efficiently manage your warehouse or storage facility, and it is quick and easy to learn.

Kardex Tauro is free for non-commercial use.
It does not require an internet connection; it runs on Windows.

Python code: weighted-average stock ledger (free download)

Python code: weighted-average stock ledger (free download)

Goods leave the shelf and the cost of what left has to be written down that same day, not at month end. When the buying price moved twice in one week, a figure recalled from memory will be wrong. This Python program reads the purchases and the sales from a file, recomputes the weighted-average cost after every movement and leaves the table ready to review or to hand over to accounting. It is meant for the owner who wants to see where the margin comes from, for the storekeeper who hands over the goods and for the bookkeeper who needs the detail without rebuilding it by hand.

It is not an inventory system and it has no screens: it is a text file with instructions that anyone can read, run and change. Sample data for two products of a hardware store travels inside the download, so the result makes sense before anything has been prepared at home.

⬇ Descargar el código (ZIP)

What comes in the package

The ZIP weighs 5288 bytes and unpacks into a single folder. Everything needed sits inside it: the commented program, the sample data, the output you should get and a short guide. There is no dependency to install and no account to create anywhere.

FileWhat it is for
kardex_promedio.pyThe program, commented step by step
datos/movimientos.csvThe sample purchases and sales of the two products
salida_ejemplo.txtThe output you should get when you run it
README.mdThe guide for the package, with the steps and the disclaimer
salida/kardex.csvThe ledger in CSV form, created by the program when it runs

What the program does

The walk-through follows a paper ledger card, in the same order as the events happened:

  1. Reads datos/movimientos.csv. Every row carries the date, the product, the movement type, the quantity, the unit cost and the document detail.
  2. Groups the movements by product and walks them in the order of the file.
  3. On every purchase it raises the units on hand and adds to the inventory the amount that was really paid.
  4. After each movement it recomputes the average cost: the value of the balance divided by the units still on hand.
  5. On every sale it values the goods at the average in force before taking them off the balance.
  6. Prints the ledger in columns, builds a summary per product with its check and saves everything to salida/kardex.csv.

How to run it

You need Python 3.11 or newer and nothing else: the program uses the standard library only, so there are no packages to install and no licence to pay for. These are the steps.

  1. Install Python from the official site and tick the box that adds it to the system.
  2. Unzip the download into any folder.
  3. Open a terminal and move into that folder with the command that changes directory.
  4. Type the order and press Enter.
python kardex_promedio.py

The table shows up in the window and the file notice comes last. The CSV lands in salida/kardex.csv, inside the same folder, ready to open in Excel or in any spreadsheet. One detail that saves time: the program finds its own folder, because it takes the folder of the program file as its reference, so it can be launched from any path and will still write the result next to itself.

The code, explained

The program stays under two hundred lines and every line carries a comment. These five parts are the ones worth understanding, because they are the ones that decide the cost.

One: money is never computed with binary decimals. Cents are handled with Decimal, not with float, and every result is rounded to two decimals with commercial rounding. That is the difference between a ledger that ties and one that drags lost cents along.

def redondear(valor: Decimal) -> Decimal:
    """Rounds to two decimals (cents) with commercial rounding."""
    return valor.quantize(CENTAVO, rounding=ROUND_HALF_UP)

Two: every purchase enters at what was actually paid. The state of a product holds three things: the units, the value and the list of rows that will be printed. On a purchase the quantity goes up, the amount paid is added to the value and the row is filled with the data of that purchase.

        if movimiento["type"] == TIPO_ENTRADA:
            # Entrada: el inventario sube por lo que realmente se pagó.
            costo = Decimal(movimiento["unit_cost"])
            valor = redondear(cantidad * costo)
            estado["cantidad"] += cantidad
            estado["valor"] += valor
            fila.update(e_cant=cantidad, e_vu=costo, e_val=valor)

Three: a sale is valued at the average in force before the movement. This is the heart of the weighted average and the order matters: the average is computed from the balance that existed before the sale was taken off. Compute it afterwards and the outgoing goods would be valued at a cost that did not exist yet, so the margin would not match reality.

        elif movimiento["type"] == TIPO_SALIDA:
            # Salida: se valora al promedio vigente ANTES de descontar el movimiento.
            promedio = redondear(estado["valor"] / estado["cantidad"]) if estado["cantidad"] else Decimal("0.00")
            valor = redondear(cantidad * promedio)
            estado["cantidad"] -= cantidad
            estado["valor"] -= valor
            fila.update(s_cant=cantidad, s_vu=promedio, s_val=valor)

Four: the balance is recomputed on every movement. Once the movement is done, the program leaves the balance in units, its average cost and its value in the row. The printed average is rounded to cents, but the balance value keeps the real sum: that is why the ledger never loses cents when it is reconciled with accounting.

        fila["saldo_cant"] = estado["cantidad"]
        # # Cost in force: value of the balance divided by the units on hand
        fila["saldo_prom"] = redondear(estado["valor"] / estado["cantidad"]) if estado["cantidad"] else Decimal("0.00")
        fila["saldo_val"] = redondear(estado["valor"])

Five: the check says out loud when something does not tie. The summary adds up purchases and sales per product and compares: when the balance equals purchases minus sales it prints TIES, otherwise REVIEW. It is the cheapest alarm an inventory can have.

        cuadra = entradas_cant - salidas_cant == estado["cantidad"]
        print(f"  {producto}")
        print(f"    Purchases : {miles(entradas_cant):>9} units {miles(entradas_val):>16}")
        print(f"    Sales      : {miles(salidas_cant):>9} units {miles(salidas_val):>16}")
        print(f"    Balance      : {miles(estado['cantidad']):>9} units {miles(redondear(estado['valor'])):>16}")
        print(f"    Check (balance = purchases - sales): {'TIES' if cuadra else 'REVIEW'}")

With those five pieces the whole program can be read. If the need appears later, a column can be added to the CSV and the free field reused for the warehouse or the supplier, without touching the computation.

What you will see on screen

The first screen repeats the program banner, tells you how many movements were read and opens the table of the first product. It looks like this:

================================================================================================================================================================
  WEIGHTED-AVERAGE STOCK LEDGER
  Didactic Python code · Kardex Tauro · kardex-tauro.muisca.co
================================================================================================================================================================
Movements read: 10

Product: Gray cement 50 kg
----------------------------------------------------------------------------------------------------------------------------------------------------------------
Date        Detail                                               In  Unit cost      In value      Out  Avg. cost Cost of sales  Balance  Avg. cost Balance value

From left to right: the date, the document detail, the quantity and unit cost of the purchase, the value of that purchase, the quantity and average cost of the sale, the cost of sales, the balance in units and the value of the balance with its average. Purchase rows and sale rows are told apart because the columns that do not apply stay empty. The final balance closes each product, and the summary with the check comes after it. In the sample data the cement starts with a purchase of 100 units at 18 000 each, then a sale of 90 units and a second purchase at 20 000: there you can watch the average move up and the next sale go out valued at the new average instead of the price of the first purchase.

Common mistakes and tips

  • The movement type must say exactly the accepted word for a purchase or a sale. The program does not guess: when it finds anything else it stops with a message instead of carrying on with a wrong figure.
  • If the CSV is edited in Excel, save it as a comma-delimited CSV and keep the column names, because the program reads each figure through them.
  • Dates are written as year, month and day, for example 2026-03-02. The program does not reorder the file: a movement with the wrong date stays in that position in the ledger.
  • When a sale leaves the balance at zero, the average is only computed while there are units on hand; the condition that avoids dividing by zero is already written inside the program.
  • When the summary prints REVIEW, there is almost always a unit cost typed wrong or a movement with the type switched. Check those two columns before anything else.
  • The output CSV can be copied and extended with more products: the program builds one block per product without a single change to the code.

When this is not enough

It is worth saying plainly what this program does and what it does not. It solves the weighted-average computation over a clean file and it shows why the order of the movements changes the cost of sales, but it does not control who types the data, it does not match the invoice against the delivery note, it does not handle several warehouses and it gives no warning when a cost looks suspiciously high. It keeps no history of changes either.

Once a business no longer wants to depend on a file that a single person maintains, the next step is Kardex Tauro, a day-to-day inventory program where the same computation happens without anyone running anything. While the volume stays low and one person handles the movements, this code is more than enough: it is free and it can be read from end to end.

⬇ Descargar el código (ZIP)

Download the package, run it on the sample data and then replace the movements file with your own. Seeing your own inventory in a table that ties is the best way to understand weighted-average cost. And if the result helps, keep it: Kardex Tauro publishes more teaching programs like this one.

Share
Link copied
Microsoft Store from Microsoft StoreDownload free
Chatea por WhatsApp