Skip to content
Operations · Inventory management module

An inventory management system that matches what's actually on the shelf

Your stock report says 70 units. The shelf says 52, and the buyer has started keeping her own list in a notebook. An inventory management system only helps if people believe it, so describe how your stock really moves and erpfly writes the ERPNext app or Odoo addon that enforces it.

ERPNext v16

Works with ERPNext v15 and v16, and Odoo 17, 18 and 19.

Features

What your inventory management module can do

Reorder points from real usage

Minimums calculated from recent issues and supplier lead time, per item and per warehouse. The job proposes a request. A person still decides to buy.

Batch and expiry rules by item group

Food, chemicals and pharma items must carry a batch and an expiry date. Hardware doesn't. The rule sits on the item group, so it isn't set item by item.

Adjustments that need a reason

Stock corrections go through the ledger with a reason code. Anything above a value you set waits for the operations manager before it posts.

Projected stock you can explain

A report that shows on-hand, reserved, ordered and projected quantity per warehouse, with a link to the transaction behind every number.

Clean opening balances

An import that loads items, warehouses and counted quantities in one dated posting, so your opening stock value is right on day one.

The output

What actually gets generated

Real files in a real repository. Here’s the typical output when someone asks for inventory management.

ERPNext / Frappe app

  • Reorder Rule DocType (item, warehouse, lead time, safety days)
  • Nightly job in hooks.py scheduler_events raising Material Requests
  • doc_events validation on Stock Entry and Stock Reconciliation
  • Custom fields on Item and Item Group as fixtures
  • Projected stock Script Report built on Bin

Odoo addon

  • stock.warehouse.orderpoint inheritance with usage-based minimums
  • Reason codes on stock.quant inventory adjustments
  • @api.constrains on stock.lot for mandatory expiry dates
  • ir.cron job for the reorder calculation
  • Inherited list and pivot views on stock.quant
usage_reorder/reorder.py
import frappe
from frappe.utils import add_days, ceil, flt, nowdate

LOOKBACK_DAYS = 60
COVER_DAYS = 30

def raise_reorder_requests():
    """Nightly, from scheduler_events. Proposes stock, never orders it."""
    for rule in frappe.get_all("Reorder Rule", fields=["item_code", "warehouse", "lead_time_days", "safety_days"]):
        used = frappe.db.sql("""
            select -sum(actual_qty) from `tabStock Ledger Entry`
            where item_code = %s and warehouse = %s and actual_qty < 0
              and is_cancelled = 0 and posting_date >= %s
        """, (rule.item_code, rule.warehouse, add_days(nowdate(), -LOOKBACK_DAYS)))[0][0]
        daily = flt(used) / LOOKBACK_DAYS
        reorder_point = daily * (rule.lead_time_days + rule.safety_days)
        projected = flt(frappe.db.get_value(
            "Bin", {"item_code": rule.item_code, "warehouse": rule.warehouse}, "projected_qty"))
        if not daily or projected >= reorder_point:
            continue
        schedule = add_days(nowdate(), rule.lead_time_days)
        frappe.get_doc({
            "doctype": "Material Request",
            "material_request_type": "Purchase",
            "schedule_date": schedule,
            "items": [{
                "item_code": rule.item_code, "warehouse": rule.warehouse,
                "qty": ceil(reorder_point - projected + daily * COVER_DAYS),
                "uom": frappe.db.get_value("Item", rule.item_code, "stock_uom"),
                "conversion_factor": 1, "schedule_date": schedule,
            }],
        }).insert(ignore_permissions=True)
Trimmed excerpt. The full module includes tests, fixtures and a README.

How it works

From a paragraph to a pull request

The long version
  1. 01

    Describe it

    In your own words. Paste the spreadsheet or a photo of the paper form if that's easier.

  2. 02

    Answer a couple of questions

    It asks only what it can't work out from your setup, like who's allowed to override.

  3. 03

    Try it on a sandbox

    A copy of your site with the module installed. Break it, then ask for changes.

  4. 04

    Merge when it's right

    Code lands as a pull request with tests. Your developer, or ours, reviews it first.

Stock numbers the buyers stopped trusting

Most inventory problems start as a habit, not a bug. A technician grabs three boxes of filters for a job and promises to sort the paperwork later. Now the system is wrong by three boxes. Give that a year and you’ve got a stock report the warehouse lead openly laughs at.

ERPNext and Odoo both track stock properly out of the box. Every movement becomes a ledger entry, valuation follows FIFO or moving average, and batches and serial numbers are standard. What neither can do is guess your rules. Which items need a batch? Who may adjust stock without a count? When does a low quantity turn into a purchase request, and for how much? Those answers are yours, and they’re the part erpfly writes.

What goes into the inventory management system we generate

On ERPNext, you get a Frappe app that sits beside ERPNext and never edits it. Typical pieces: custom fields on Item and Item Group shipped as fixtures, a small DocType for your reorder rules, doc_events in hooks.py that check Stock Entries before they post, and a Script Report that reads projected quantity from the Bin table. Nightly work goes into scheduler_events, so anyone can see what runs and when.

On Odoo, it’s an addon that extends stock.quant, stock.move and stock.warehouse.orderpoint through _inherit, with views that inherit the standard ones and access rights in ir.model.access.csv. Inventory itself is part of Community, so none of this needs Enterprise.

One caveat. Both platforms already do fixed min/max reordering. If a fixed minimum works for an item, keep it.

Worked example: a spare parts distributor outgrows fixed minimums

Picture a distributor with about 4,000 SKUs of bearings, belts and seals across two warehouses. Fixed reorder levels were fine when the range was small. Now half of them are stale. Fast movers run out, slow movers sit for a year.

The buyer, Leena, already has a rule. It just lives in her head:

  • Take the last 60 days of issues for each item in each warehouse, divided by 60, as daily use.
  • Reorder point is daily use times (supplier lead time + 7 safety days).
  • If projected stock falls below that, request enough to get back to the reorder point plus 30 days of cover.

Take one V-belt. It shipped 240 units in 60 days, so 4 a day. The supplier needs 14 days, so the reorder point is 4 × 21 = 84. The Bin says projected quantity is 70. The nightly job raises a Material Request for 84 − 70 + 120 = 134 units. Leena looks at it over coffee and turns it into a Purchase Order. Or she doesn’t, because she knows a big customer just went bust. The job proposes. A human buys.

The code on this page is the core of that job. Around it sit a Reorder Rule DocType where Leena sets lead time and safety days, and a report that shows why each request was raised.

Requests we’d argue against

Negative stock “just for now”. Both platforms let you switch it on. Valuation gets messy fast, and untangling it later costs someone a week. We’d rather fix whatever makes people want it.

Serial numbers on every screw. Serials make sense for items with warranties or service history. For consumables, a batch is plenty. Often nothing is.

A quick “fix quantity” button. Corrections belong in Stock Reconciliation (ERPNext) or an inventory adjustment (Odoo), with a reason. Anything that edits quantities outside the ledger will eventually meet your accountant, and it won’t go well.

Syncing five marketplaces in week one. Get the internal numbers right first.

Leaving the spreadsheet behind

Most people arrive with an Excel file, or three. We generate an import that loads items, warehouses and counted quantities as a single dated posting on your go-live day, so opening value is booked once. Do a physical count the weekend before. Yes, really. A spreadsheet you don’t trust becomes a system you don’t trust, just with nicer menus.

Where inventory meets the rest of the ERP

Stock matters because something moves it. Receipts come from purchase management, deliveries follow sales orders, and if you need bins, putaway and pick lists inside each building, that’s warehouse management. Distributors should read how we approach ERP for distribution businesses. Still choosing a platform? The ERPNext vs Odoo comparison covers how each one handles stock.

Guides and terms for inventory management

Inventory management module questions

Something missing? Email hello@erpfly.com and a person will answer.

ERPNext and Odoo already manage inventory. Why would I generate anything?

If the standard reorder levels, batches and stock counts fit, you shouldn't. People come to us when their rule is different, like usage-based minimums, approval on adjustments, or expiry rules by product family. We generate only that difference and leave the rest of the stock module alone.

Will a custom inventory module break when I upgrade?

It lives in its own Frappe app or Odoo addon and doesn't touch core files. Stock is a busy area in both platforms, so run the included tests after a major version jump and read the release notes for stock changes. Budget an hour for it.

Can we move our stock from Excel, Tally or QuickBooks?

Yes. We generate an import for items, warehouses and opening quantities, posted as one dated Stock Reconciliation in ERPNext or an inventory adjustment in Odoo. Count the stock physically first, though. Importing numbers you don't trust just gives them a nicer font.

Do barcode scanners work with this?

Any scanner that types like a keyboard works in both ERPNext and Odoo forms. Odoo's dedicated Barcode app for handhelds is Enterprise only. If you need scan-heavy picking by bin, look at the warehouse management module instead.

Who owns the code, and can our own developer change it?

You own it. It's a normal app or addon in your Git repository with a README, so any Frappe or Odoo developer can pick it up. Nothing stops working if you leave erpfly.

How long does it take?

A first version of a reorder module usually appears within minutes. Expect a week or two of letting it propose requests alongside your buyer before you trust it, and that week is well spent.

Your next module is one paragraph away

Write it the way you’d explain it to a new hire. We’ll turn it into an app you can read, test and install.

ERPNext v16

Create your account

Free to start. No card needed.

By signing up you agree to our terms and privacy policy.