Inventory Management (Open Source!)


Hi everyone,

For a while now I’ve been developing an open source inventory management system. I know from a number of posts on this forum that people are often looking for good solutions to this task - I’d be pleased if my software helps my fellow KiCad users! Since releasing it I have received some feedback from people using it for many varied applications!

This project is aiming to fulfill requirements for small-medium businesses (or hobbyists) looking to keep track of parts and stock, without resorting to large, complex enterprise software that is expensive and hard to use. InvenTree is designed to be intuitive, and allow for simple procedural flow in tracking stock.

While not designed exclusively for electronic component management, this is where it excels. Check out some of the screenshots below for some examples of what it can do.

I am keen for people to try it out and help make improvements.

Key Functions

  • Manage parts in multiple hierarchical categories
  • Keep track of stock in multiple hierarchical locations
  • Record stock movements
  • Track parts in batches
  • Track uniquely serialized parts
  • BOM management
  • Build parts from other parts (using BOM data) and allocate parts from stock
  • Purchase parts from multiple suppliers
  • Keep track of which stock came from which purchase order / supplier
  • Export data to external applications
  • Import data (e.g. BOM from KiCad)

Technical Details

  • Written in Python using the Django framework
  • Web-based front end which can be served locally or on the cloud.
  • Works with multiple database backends (SQLite / MySQL / PostgreSQL)
  • Inbuilt JSON API for interfacing with external applications
  • Python library for direct access (using aforementioned JSON API)



Interesting and thanks for the effort you put in for this. I poked around the docs but could not see how you handle 2nd source/part obsolescence. Additionally, I couldnt determine if this is a standalone tool or linked to KiCAD in some way. For example, could I have an IPN field in KiCAD that when clicked on/in brings up this tool so I can select the appropriate part?


The user documentation is the weak point of the system currently. Each “part” can be linked to multiple supplier-part objects. e.g.

Additionally, I couldnt determine if this is a standalone tool or linked to KiCAD in some way. For example, could I have an IPN field in KiCAD that when clicked on/in brings up this tool so I can select the appropriate part?

At this stage it is a completely standalone tool (although I do use it in close collaboration with KiCad all the time, but it is a manual export/import process).

I would love to support a closer linking between the two programs, but I haven’t seen a clear path to that yet.


probs installing - at the point of “make superuser”:

python3 InvenTree/ createsuperuser

You have 81 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, authtoken, build, common, company, contenttypes, order, part, sessions, stock.
Run ‘python migrate’ to apply them.

Traceback (most recent call last):
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/”, line 84, in _execute
return self.cursor.execute(sql, params)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/sqlite3/”, line 383, in execute
return Database.Cursor.execute(self, query, params)
sqlite3.OperationalError: no such table: auth_user

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File “InvenTree/”, line 22, in
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/management/”, line 381, in execute_from_command_line
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/management/”, line 375, in execute
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/management/”, line 323, in run_from_argv
self.execute(*args, **cmd_options)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/contrib/auth/management/commands/”, line 61, in execute
return super().execute(*args, **options)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/core/management/”, line 364, in execute
output = self.handle(*args, **options)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/contrib/auth/management/commands/”, line 82, in handle
default_username = get_default_username()
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/contrib/auth/management/”, line 140, in get_default_username
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/models/”, line 82, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/models/”, line 402, in get
num = len(clone)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/models/”, line 256, in len
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/models/”, line 1242, in _fetch_all
self._result_cache = list(self._iterable_class(self))
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/models/”, line 55, in iter
results = compiler.execute_sql(chunked_fetch=self.chunked_fetch, chunk_size=self.chunk_size)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/models/sql/”, line 1100, in execute_sql
cursor.execute(sql, params)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/”, line 99, in execute
return super().execute(sql, params)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/”, line 67, in execute
return self._execute_with_wrappers(sql, params, many=False, executor=self._execute)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/”, line 76, in _execute_with_wrappers
return executor(sql, params, many, context)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/”, line 84, in _execute
return self.cursor.execute(sql, params)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/”, line 89, in exit
raise dj_exc_value.with_traceback(traceback) from exc_value
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/”, line 84, in _execute
return self.cursor.execute(sql, params)
File “/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/sqlite3/”, line 383, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: auth_user
make: *** [superuser] Error 1

I made no mods to the config.yaml file btw


As per the install guide (,

you have to run make migrate before creating a superuser account.

This is required to create the necessary tables in the database.


i did, following your install instructions to the tee. want me to run it again?

I did run make migrate again, got this result:
python3 InvenTree/ makemigrations common
No changes detected in app ‘common’
python3 InvenTree/ makemigrations company
No changes detected in app ‘company’
python3 InvenTree/ makemigrations part
No changes detected in app ‘part’
python3 InvenTree/ makemigrations stock
No changes detected in app ‘stock’
python3 InvenTree/ makemigrations build
No changes detected in app ‘build’
python3 InvenTree/ makemigrations order
No changes detected in app ‘order’
python3 InvenTree/ makemigrations
No changes detected
cd InvenTree && python3 migrate
Operations to perform:

Apply all migrations: admin, auth, authtoken, build, common, company, contenttypes, order, part, sessions, stock

Running migrations:
No migrations to apply.
cd InvenTree && python3 migrate --run-syncdb

Operations to perform:
Synchronize unmigrated apps: corsheaders, crispy_forms, dbbackup, django_cleanup, django_filters, import_export, messages, mptt, qr_code, rest_framework, staticfiles

Apply all migrations: admin, auth, authtoken, build, common, company, contenttypes, order, part, sessions, stock

Synchronizing apps without migrations:
Creating tables…
Running deferred SQL…

Running migrations:
No migrations to apply.
python3 InvenTree/ check
System check identified no issues (0 silenced).

cd InvenTree && python3 collectstatic
You have requested to collect static files at the destination
location as specified in your settings:
/Users/joost/Library/Mobile Documents/com~apple~CloudDocs/inventree/InvenTree/static

This will overwrite existing files!
Are you sure you want to do this?
Type ‘yes’ to continue, or ‘no’ to cancel: yes
0 static files copied to ‘/Users/joost/Library/Mobile Documents/com~apple~CloudDocs/inventree/InvenTree/static’, 280 unmodified.

Then ran make superuser again which returne the same failure as already mentioned. Should I now do ‘python migrate’ as the error suggests or is that the same as make migrate?


Recently had a tour of a major USA based assembly house.

Depending upon parts, and manufactures, attrition numbers per setup/take-down on the line would help for Kanban inventory management.

On Edit: I am hand-soldering my designs so far, but I had a Digi-Key cut tape of Kemet SMD capacitors, and the top tape lost it’s adhesiveness to the parts tray of the tape. I recovered a few of them by hand, and I think the vacuum may have got the rest… but there may still be one hiding in the corner reporting my every movement.


That appeared to run OK for me. When the next step failed I went back and realized the ‘make migrate’ failed. All indications had scrolled off my screen prior and I didn’t realize. Not complaining, just a data point. I don’t have time at the moment to do more with it. I’ve been wanting to do something of late with inventory so I gave it a quick try. I’ll come back to it when I have more time.


Creating a part;

a) I see I can enter my own IPN. Thats good because it allows me to import my current system. But there is no control over this field is there? I’d expected I need to set up some sort of template so that we don’t have unique number clashes, get automated new IPNs etc.

b) I would prefer to have two description fields: a short one and a detailed one. Example for a TLV4316 I’d show:
Detail: General Purpose Amplifier Circuit Rail-to-Rail SOT-23-5
Pretty much what you can find in Mouser/Digikey catalogs. Or do you propose to use ‘notes’ for the detail?

c) In the part, this URL which is valid on my system (macOS) is not accepted by invenTree: x-devonthink-item://1C1E931E-1A30-4515-BCF9-45CDF39B56EC

d) Could not figure out how to get pricing setup. Can it download from the supplier(s)?

e) No delete function on parts?

f) For deletable elements (suppliers, catalogs etc.) please have more space between delete and edit buttons - they are too close for comfort.

You’ve got some nice basic functionality, certainly useful even after digging into it only skin deep. It does feel a little “modally” i.e., having to navigate to a part, then back up, other part. Cannot see certain data sets side by side etc. I know it is a bit the nature of apps like these but would be nice to see you explore more navigation methods.

I’ll complain more when I have more complaints (haha). Appreciate your efforts!


Ugh… I just went through a brief 2 day summary training on LEAN (my first LEAN training…). The instructor’s accent was such that every time he said Kanban I heard it as “Combine” and I had to remind myself that we weren’t talking about farming equipment…

I hate to say it, but my current opinion of LEAN is a bunch of common sense wrapped up in buzz words and jargon.


There is concept of overage/wastage for each item in a Build - this would probably address this for you?

Unfortunately I can’t do much about your lurking capacitors. Perhaps leave some flux paste out overnight to flush them out of their hiding places?


The IPN field is unstructured text. This was a conscious decision to allow the formatting to be left up to the particular company policy. One thing I could add is an optional “IPN Format” field in the master settings and the IPN must match that field. The IPN field does actually have a uniqueness constraint, but it is in combination with other fields. For example, you can have multiple parts with the same IPN if they have a different revision code.

You could certainly use the notes field for this.

TBH if I look at that I wouldn’t think it was a URL either :wink: but I’m not a mac person. I’ll have to look into this one.

Add a SupplierPart link against a part. Then, you can add price breaks against that SupplierPart. Quantity pricing is calculated across all available SupplierPart pricing data. Also, if you have an assembly you can calculate the parts cost for that assembly, based on the supplier part pricing for each item in the BOM.

There is, but it is hidden unless the part is first marked as “Inactive” (which you can do in the part editing dialog). Then the delete button is visible. The aim is to make important records harder to delete from the system.

Live life on the edge! (But just for you, I will consider this)

One thing I would like to improve upon is the modality in a lot of the views. Using the AJAX API to do “live” updates to the page with a single button press is a lot quicker in terms of UX. I have learned a lot about that just to get the software where it is now.

If you see any other problems, please raise an issue on GitHub so that I can keep track of them :slight_smile:


Its an application URL/URI so the x-devonthink-item describes the app that is supposed to handle the stuff right from :// DevonThink is a macOS app where I keep all my docs/datasheets. App URLs exists for Windows/Linux also. A URL is not required to start with http - that simply means the local browser app is supposed to handle the rest of the string. Perhaps have a look at this:

See you on github.


@joost I managed to replicate the problem here -

Thanks for pointing this out.


@SchrodingersGat do we:
a) import/export from/to CSV? All data aspects, not just parts. I have just too many parts in my current “system” (spreadsheet) which I don’t want to move over manually.
b) do you have plans to import pricing (a la OctoPart) ? I can imagine such functionality to be non-trivial and possibly requiring digikey/mouser/avnet/etc licensing. So perhaps a step in between is to be able to import pricing by part with csv?
c) our little team is distributed i.e. we work in different locations mostly. Naturally, the data should be centrally accessible - any guides on how to set this up?
d) you mention in the docs that invenTree is meant for the hobbyist market. Can you give us a bit of your thinking where you draw the line - what you definately do not consider to implement? I am curious as I have worked with big inventory/configuration management systems that imho were way too costly for their value with bloated functionality that made one hate having to work with it. I can see invenTree at some point fitting in a market beyond hobbyist but not stupid bloated. Just curious where you plant to take it.


Of course, I’m not a monster :wink: There are multiple ways of importing bulk data:

Admin Interface

There is an admin interface (available to users in the “staff” group) which provides low level control of all the data. You can edit any record in the database, as well as performing bulk data import/export for each table.

BOM Import

Bills of Material can be constructed manually, by adding each row individually. But we aren’t animals, there’s a better way.

You can import a BOM file (csv / xls) and the InvenTree web interface will step you through the process of associating each line item with an entry in the database (or creating a new entry if it does not exist). The part selection is assisted using fuzzy-string matching so the part naming in the external BOM does not have to match exactly.

Fixture Import

Through the django backend you can manually import data from an external data file (csv / json / etc) but this is a lot more difficult and not recommended.


Yes, there are plans for this. But you are right that it is a complex problem to solve. at the very least each install would need their own API key to access the data. There is also the issue of end-user agreements for using API data - some do not really allow “caching” of data - but I am yet to fully understand what this means.

It is also difficult to program something like this to function in the background, without someone keeping an eye on the script. There is a potential to inject a lot bad data without any user input.

One option is to provide API integration as “add-on” scripts which you run separately to the web interface. The InvenTree Python API - - would be perfect for this.

Most of these external APIs (digikey / octopart / etc) provide sample python code and this could be linked very simply with the InvenTree python code.

I envision that some sample scripts could be provided and the user just has to provide their own API key.


Where the data is actually served is up to the particular installation requirements:

Local Install

For a simple implementation this could be running locally on your PC. I have a development database serving from my PC (running under WSL on Windows).

Local Network

If installed on a local network server it could be accessible to anyone on the network.


You could run InvenTree on some globally-accessible server e.g. AWS. Currently this is how we are running our work install.

The actual infrastructure that you use to serve the database is not prescribed. Here’s the basic set up I am running.

  • InvenTree database in MySQL
  • Gunicorn serves the web application and runs the python / database interface
  • Apache as a webserver to direct traffic to Gunicorn
  • AWS does all the hosting / storage / URL lookup magic

Exactly how far you would want to take this depends on your specific requirements. There are plenty of guides on setting this up for a generic django application.


This project started as a result of reviewing these existing systems, and deciding that actually using them was far too cumbersome, and it would be too tough to convince everyone to keep track of the data. Without good data the whole system falls down - so it should be easy to use as a priority.

My chief concern is retaining the ease of use. At the core of the project is:

  • Understanding what parts you have in stock
  • Knowing where those parts are
  • Knowing what you can build, and what you need to order to fulfill requirements

This core functionality is what I want to focus on supporting. There will be associated functionality of course and I’m happy to consider any changes or features but not at the cost of these features.


I just installed from the latest code - looks like revisions as of 2 hours before I D/L, commit afdea81 - and I get a very similar failure. For me, it happens when I run the development server.
" You have 82 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, authtoken, build, common, company, contenttypes, order, part, sessions, stock.

Run ‘python migrate’ to apply them."

Blew the folder away, cloned again, followed instructions again - same results. Any suggestions?