Developing an Inventory management system with Django and bootstrap.
Introduction.
A full working project code is hosted on this repository
Inventory => Inventory, also known as stock, refers to goods a business owns, with the aim to sell or use them in production.
Inventory management system => Inventory management software is a computer system that helps manage inventory, from keeping stock levels to issuing and receiving stock, among many other tasks.
Django => Django is a high-level web framework developed using python. Django comes preloaded with standard tools to handle common web development, such as user management, admin site, content management, etc., making it ideal for developing web systems with minimal effort.
Bootstrap => Bootstrap is a popular CSS framework for building beautiful and responsive web pages.
Environment setup.
Installing python
To begin with, you'll need to have a working python installation on your computer. Go to https://www.python.org/downloads/ and get it for your PC/Mac. The instructions to install it are available on the official python website.
Installing git
Git is a popular version control system. It keeps track of changes you make to your code, which is handy when you want to revert to a previous version (mostly after a mess-up). Git also helps us push our code to online code hosting services such as Github and Gitlab, which allow developers to collaborate on a project.
In addition to keeping track of changes in our code, git on Windows allows you to use commands such as ls, execute bash scripts, etc., making development easy.
You can get git at https://git-scm.com/downloads. Instructions for installation are on the site as well.
Installing Django.
Once you have installed python and git, create a folder named inventory-management-system
anywhere on your machine. Change to the inventory folder, then right-click and select git bash here
. A git bash will open in the inventory folder.
Create a virtual environment:-
Type
python -m venv env
. This command creates a virtual environmentenv
where we will install the project's dependencies.Activate the virtual environment:-
On Windows
$ env\Scripts\activate
On Linux/Mac
$ source /env/bin/activate
Install Django:-
pip install django~=4.0.0
Install Django crispy form, which integrates Django nicely with Bootstrap
$ pip install django-crispy-forms~=2.0
$ pip install crispy-bootstrap5~=0.7
We can start developing our system now that we have installed all the required packages.
Structure of a Django application.
Django has tools to help the developer manage tasks such as generating files, interacting with the ORM, running the development server, etc.
To generate our project, we will use the django-admin
command as follows:
$ django-admin startproject IMS
The above command will create a Django project called inventory with the necessary python files, as shown below.
The __init__.py
indicates that the directory is a python package.
The asgi.py
file is used to configure Django to run using the ASGI, which is an emerging for asynchronous web servers, by default django uses WSGI.
You can read more about asgi here, https://asgi.readthedocs.io/en/latest/
The settings.py
contains setting for your django project.
The urls.py
contain the URL declarations for your project.
The wsgi.py
file is used as entry point to web servers such as Nginx and Apache.
You'll also note that there is a manage.py
file, this is a command line utility that helps you interact with your django project.
Now that everything is fully setup, change directory to the IMS directory.
$ cd IMS
To run the development server to test out our application, type the command below, then visit 127.0.0.1:8000
on your browser.
$ python manage.py runserver
You should see a cool site similar to the one above.
Creating the Inventory app
Now that our project setup is complete, let us create the inventory app.
$ python manage.py startapp inventory
A project vs an app
As per the django documentation "An app is a web application that does something – e.g., a blog system, a database of public records or a small poll app. A project is a collection of configuration and apps for a particular website. A project can contain multiple apps. An app can be in multiple projects."
The command generates an app with the following structure.
Creating the models.
"A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you’re storing. Generally, each model maps to a single database table.", Django documentation.
Django models are written in the models.py file, we will create four models for our project, namely UserRegistry, ProductForm and OrderForm.
# inventory/models.py
from email.policy import default
from django.db import models
from django.contrib.auth.models import User
CATEGORY = (
("Stationary", "Stationary"),
("Electronics", "Electronics"),
("Food", "Food"),
)
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
physical_address = models.CharField(max_length=40, null=True)
mobile = models.CharField(max_length=12, null=True)
picture = models.ImageField(default="avatar.jpeg", upload_to="Pictures")
def __str__(self) -> str:
return self.user.username
class Product(models.Model):
name = models.CharField(max_length=100, null=True)
category = models.CharField(max_length=20, choices=CATEGORY, null=True)
quantity = models.PositiveIntegerField(null=True)
description = models.CharField(max_length=200, null=True)
def __str__(self) -> str:
return self.name
class Order(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
created_by = models.ForeignKey(User, models.CASCADE, null=True)
order_quantity = models.PositiveIntegerField(null=True)
date = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
return f"{self.product} ordered quantity {self.order_quantity}"
Creating forms from models.
"If you’re building a database-driven app, chances are you’ll have forms that map closely to Django models. For instance, you might have a BlogComment model, and you want to create a form that lets people submit comments. In this case, it would be redundant to define the field types in your form, because you’ve already defined the fields in your model.", Django documentation.
Create the forms.py
file inside the inventory
folder and add the following code.
# inventory/forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from inventory.models import Product, Order
class UserRegistry(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = [
"username",
"first_name",
"last_name",
"email",
"password1",
"password2",
]
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = ["name", "category", "quantity", "description"]
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ["product", "order_quantity"]
Creating views.
"A view function, or view for short, is a Python function that takes a web request and returns a web response. This response can be the HTML contents of a web page, or a redirect, or a 404 error, or an XML document, or an image . . . or anything, really. The view itself contains whatever arbitrary logic is necessary to return that response. This code can live anywhere you want, as long as it’s on your Python path. There’s no other requirement–no “magic,” so to speak.", Django documentation.
# inventory/views.py
from django.shortcuts import render, redirect, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from inventory.forms import UserRegistry, ProductForm, OrderForm
from inventory.models import Product, Order
@login_required
def index(request):
orders_user = Order.objects.all()
users = User.objects.all()[:2]
orders_adm = Order.objects.all()[:2]
products = Product.objects.all()[:2]
reg_users = len(User.objects.all())
all_prods = len(Product.objects.all())
all_orders = len(Order.objects.all())
context = {
"title": "Home",
"orders": orders_user,
"orders_adm": orders_adm,
"users": users,
"products": products,
"count_users": reg_users,
"count_products": all_prods,
"count_orders": all_orders,
}
return render(request, "inventory/index.html", context)
@login_required
def products(request):
products = Product.objects.all()
if request.method == "POST":
form = ProductForm(request.POST)
if form.is_valid():
form.save()
return redirect("products")
else:
form = ProductForm()
context = {"title": "Products", "products": products, "form": form}
return render(request, "inventory/products.html", context)
@login_required
def orders(request):
orders = Order.objects.all()
print([i for i in request])
if request.method == "POST":
form = OrderForm(request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.created_by = request.user
instance.save()
return redirect("orders")
else:
form = OrderForm()
context = {"title": "Orders", "orders": orders, "form": form}
return render(request, "inventory/orders.html", context)
@login_required
def users(request):
users = User.objects.all()
context = {"title": "Users", "users": users}
return render(request, "inventory/users.html", context)
@login_required
def user(request):
context = {"profile": "User Profile"}
return render(request, "inventory/user.html", context)
def register(request):
if request.method == "POST":
form = UserRegistry(request.POST)
if form.is_valid():
form.save()
return redirect("login")
else:
form = UserRegistry()
context = {"register": "Register", "form": form}
return render(request, "inventory/register.html", context)
Inside the inventory directory, we are going to create a urls.py
file, the file will contain URL definitions for our app, which we will later link with the project's urls.py
file in the IMS
directory.
$ touch urls.py
Add the following code to the file
# inventory/urls.py
from django.urls import path
from inventory import views
urlpatterns = [
path("dash/", views.index, name="dash"),
path("products/", views.products, name="products"),
path("orders/", views.orders, name="orders"),
path("users/", views.users, name="users"),
path("user/", views.user, name="user"),
path("register/", views.register, name="register"),
]
Adding the inventory/urls.py
to IMS/urls.py
Modify the project urls.py file as follows.
# IMS/urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.auth import views as auth
from django.urls import include, path
urlpatterns = [
path("/", include("inventory.urls")),
path("admin/", admin.site.urls),
path(
"", auth.LoginView.as_view(template_name="inventory/login.html"), name="login"
),
path(
"logout/",
auth.LogoutView.as_view(template_name="inventory/logout.html"),
name="logout",
),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Creating the admin.py file
The admin.py contains instructions on how to display the models in the admin site, we will register all our models in the admin site.
# inventory/admin.py
from django.contrib import admin
from inventory.models import Product, Order, UserProfile
admin.site.site_header = "Inventory Admin"
class ProductAdmin(admin.ModelAdmin):
model = Product
list_display = ("name", "category", "quantity")
list_filter = ["category"]
search_fields = ["name"]
class OrderAdmin(admin.ModelAdmin):
model = Order
list_display = ("product", "created_by", "order_quantity", "date")
list_filter = ["date"]
search_fields = ["product"]
class UserProfileAdmin(admin.ModelAdmin):
model = UserProfile
list_display = ("user", "physical_address", "mobile", "picture")
list_filter = ["user"]
search_fields = ["user"]
admin.site.register(Product, ProductAdmin)
admin.site.register(Order, OrderAdmin)
admin.site.register(UserProfile, UserProfileAdmin)
Creating migrations
"Migrations are Django’s way of propagating changes you make to your models (adding a field, deleting a model, etc.) into your database schema. They’re designed to be mostly automatic, but you’ll need to know when to make migrations, when to run them, and the common problems you might run into.", Django documentation
$ python manage.py makemigrations
$ python manage.py migrate
Creating a super user.
A super user account allows us to login and manage other users through the admin interface.
The super user account is created as follows.
$ python manage.py createsuperuser
Now if you run the development server and navigate to http://127.0.0.1:8000/admin/ you will be able to see login with your created user.
Building the interface.
If you go to http://127.0.0.1:8000/ you will note that the site is displaying an error message saying that the template is not found.
Create a folder called templates
inside inventory
, and then inside the templates
create another folder called inventory
.
Inside the inventory folder, create the following HTML files base.html, index.html, login.html, logout.html, orders.html, products.html, register.html, user.html, users.html.
base.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">
</head>
<body>
{% if user.is_authenticated and user.is_staff and user.is_superuser%}
<nav class="navbar navbar-expand-lg bg-light">
<div class="container">
<a class="navbar-brand" href="{% url 'dash' %}">Dashboard</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'products' %}">Products</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'orders' %}">Orders</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'users' %}">Users</a>
</li>
</ul>
<ul class="navbar-nav ml-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'user' %}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'logout' %}">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
{% elif user.is_authenticated %}
<nav class="navbar navbar-expand-lg bg-light">
<div class="container">
<a class="navbar-brand" href="{% url 'dash' %}">Dashboard</a>
<div class="collapse navbar-collapse" id="navbarSupportedContent">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'products' %}">Products</a>
</li>
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'orders' %}">Orders</a>
</li>
</ul>
<ul class="navbar-nav ml-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'user' %}">Profile</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'logout' %}">Logout</a>
</li>
</ul>
</div>
</div>
</nav>
{% else %}
<nav class="navbar navbar-expand-lg bg-light">
<div class="container">
<ul class="navbar-nav ml-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="{% url 'register' %}">Register</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="{% url 'login' %}">Login</a>
</li>
</ul>
</div>
</div>
</nav>
{% endif %}
{% block content %}
{% endblock %}
<div class="bg-light">
<footer class="py-3 my-4">
<p class="text-center text-muted">© 2022 Inventory App, Inc.</p>
</footer>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js"
integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2"
crossorigin="anonymous"></script>
</body>
</html>
index.html
{% extends 'inventory/base.html' %}
{% block title %}{{ home }} {% endblock %}
{% block content %}
{% if user.is_authenticated and user.is_staff and user.is_superuser %}
<div class="container my-4">
<div class="row align-items-start">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h1 class="text-center">Statistics</h1>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-4">
<div class="card text-center p-3">
<h4>Registered Users</h4>
<h3><span class="badge bg-primary rounded-pill">{{ count_users }}</span></h3>
</div>
</div>
<div class="col-md-4">
<div class="card text-center p-3">
<h4>Total Orders</h4>
<h3><span class="badge bg-primary rounded-pill">{{ count_orders }}</span></h3>
</div>
</div>
<div class="col-md-4">
<div class="card text-center p-3">
<h4>All Products <i class="fa-thin fa-users"></i></h4>
<h3><span class="badge bg-primary rounded-pill">{{ count_products }}</span></h3>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container my-4">
<div class="row align-items-start">
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h1 class="text-center">Users</h1>
</div>
<div class="card-body">
<table class="table">
<thead class="table-light">
<tr>
<th scope="col">User Name</th>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.username }}</td>
<td>{{ user.first_name }}</td>
<td>{{ user.last_name }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a class="nav-link text-primary active" aria-current="page" href="{% url 'users' %}">View All
Staff</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h1 class="text-center">Orders</h1>
</div>
<div class="card-body">
<table class="table">
<thead class="table-light">
<tr>
<th scope="col">Product</th>
<th scope="col">Quantity</th>
<th scope="col">Date</th>
</tr>
</thead>
<tbody>
{% for order in orders_adm %}
<tr>
<td>{{ order.product }}</td>
<td>{{ order.order_quantity }}</td>
<td>{{ order.date.date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a class="nav-link text-primary active" aria-current="page" href="{% url 'orders' %}">Go to
Orders</a>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h1 class="text-center">Products</h1>
</div>
<div class="card-body">
<table class="table">
<thead class="table-light">
<tr>
<th scope="col">Name</th>
<th scope="col">Quantity</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.quantity }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<a class="nav-link text-primary active" aria-current="page" href="{% url 'products' %}">Go to
Products</a>
</div>
</div>
</div>
</div>
</div>
</div>
{% else %}
<div class="container my-4">
<div class="row">
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h4 class="text-center">Make an Order</h4>
</div>
<div class="card-body">
<a class="btn btn-info" href="{% url 'orders' %}">Create Order</a>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h4 class="text-center">Here is a list of your Orders</h4>
</div>
</div>
<table class="table">
<thead>
<tr>
<th scope="col">Ordered Product</th>
<th scope="col">Created by</th>
<th scope="col">Quantity</th>
<th scope="col">Order Date</th>
</tr>
</thead>
<tbody>
{% for order in orders %}
{% if order.created_by.username == user.username %}
<tr>
<td>{{ order.product }}</td>
<td>{{ order.created_by.username }}</td>
<td>{{ order.order_quantity }}</td>
<td>{{ order.date }}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
{% endblock %}
login.html
{% extends 'inventory/base.html' %}
{% block title %}{{ register }} {% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="row mt-5">
<div class="col-md-3">
<h4>Login</h4>
<hr>
<form method="POST">
{% csrf_token %}
{{ form | crispy }}
<br>
<input class="btn btn-info" type="submit" value="Login">
</form>
</div>
</div>
</div>
{% endblock %}
logout.html
{% extends 'inventory/base.html' %}
{% block title %}{{ register }} {% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="alert">
<h4>Logged out</h4>
<a href="{% url 'login' %}">Login</a>
</div>
</div>
{% endblock %}
orders.html
{% extends 'inventory/base.html' %}
{% block title %}{{ title }} {% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8">
<table class="table">
<thead>
<tr>
<th scope="col">Ordered Product</th>
<th scope="col">Created by</th>
<th scope="col">Quantity</th>
<th scope="col">Order Date</th>
</tr>
</thead>
{% if user.is_staff and user.is_superuser %}
<tbody>
{% for order in orders %}
<tr>
<td>{{ order.product }}</td>
<td>{{ order.created_by.username }}</td>
<td>{{ order.order_quantity }}</td>
<td>{{ order.date }}</td>
</tr>
{% endfor %}
</tbody>
{% else %}
<tbody>
{% for order in orders %}
{% if user.username == order.created_by.username %}
<tr>
<td>{{ order.product }}</td>
<td>{{ order.created_by.username }}</td>
<td>{{ order.order_quantity }}</td>
<td>{{ order.date }}</td>
</tr>
{% endif %}
{% endfor %}
</tbody>
{% endif %}
</table>
</div>
<div class="col-md-4">
<form method="POST">
<h4>Create a New Order</h4>
<hr>
{% csrf_token %}
{{ form|crispy}}
<br>
<button type="submit" class="btn btn-primary">Create Order</button>
</form>
</div>
</div>
</div>
{% endblock %}
products.html
{% extends 'inventory/base.html' %}
{% block title %}{{ header }} {% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-8">
<table class="table">
<thead>
<tr>
<th scope="col">Product Name</th>
<th scope="col">Quantity</th>
<th scope="col">Category</th>
<th scope="col">Description</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.quantity }}</td>
<td>{{ product.category }}</td>
<td>{{ product.description }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% if user.is_staff and user.is_superuser %}
<div class="col-md-4">
<form method="POST">
<h4>Add a New Product</h4>
<hr>
{% csrf_token %}
{{ form|crispy}}
<br>
<button type="submit" class="btn btn-primary">Add</button>
</form>
</div>
{% else %}
<div class="col-md-4">
</div>
{% endif %}
</div>
</div>
{% endblock %}
register.html
{% extends 'inventory/base.html' %}
{% block title %}{{ register }} {% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="row">
<div class="col-md-6 offset-md-3">
<h3>Create Account</h3>
<hr>
<form method="POST">
{% csrf_token %}
{{ form | crispy }}
<input class="btn btn-info" type="submit" value="Signup">
</form>
</div>
</div>
</div>
{% endblock %}
user.html
{% extends 'inventory/base.html' %}
{% block title %}{{ profile }} {% endblock %}
{% block content %}
<div class="container my-4">
<div class="row align-items-start">
<div class="col-md-8">
<div class="card">
<div class="card-header">
<h1 class="text-center">User's Profile</h1>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-8">
<div class="card text-center p-3">
<h4>Profile Information</h4>
<br>
<table class="table bg-white table-borderless">
<tbody>
<tr>
<th scope="col">User Name</th>
<td scope="col">{{ user.username }}</td>
</tr>
<tr>
<th scope="col">Full Name</th>
<td scope="col">{{ user.first_name }} {{user.last_name}}</td>
</tr>
<tr>
<th scope="row">Email</th>
<td>{{ user.email }}</td>
</tr>
<tr>
<th scope="row">Phone</th>
<td>{{ user.userprofile.mobile }}</td>
</tr>
<tr>
<th scope="row">Address</th>
<td>{{ user.userprofile.physical_address }}</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="col-md-4">
<img style="border-radius: 50%; width: auto; height: auto;" class="img-thumbnail" src="{{ user.userprofile.picture.url }}" alt="profile picture">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
users.html
{% extends 'inventory/base.html' %}
{% block title %}{{ title }} {% endblock %}
{% block content %}
<div class="container">
<table class="table">
<thead>
<tr>
<th scope="col">First Name</th>
<th scope="col">Last Name</th>
<th scope="col">Email</th>
<th scope="col">Address</th>
</tr>
</thead>
<tbody>
{% for user in users %}
<tr>
<td>{{ user.first_name}}</td>
<td>{{ user.last_name }}</td>
<td>{{ user.email }}</td>
<td>{{ user.userprofile.physical_address }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}
Additional settings
Add inventory
, crispy_forms
and crispy_bootstrap5
under the INSTALLED_APPS
and add the corresponding settings as shown below in settings.py
Next add settings to handle static media files as shown
Next, collect the static files, this is done to allow for easy serving of the files when hosting the app
$ python manage.py collectstatic
Now if you run the server again and visit the homepage again you will be met with a cool looking login page.
Subscribe to my newsletter
Read articles from Peter Mwangi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Peter Mwangi
Peter Mwangi
I am a Systems Administrator mainly working with Odoo ERP, Linux, and Postgresql. I am passionate about anything tech and spend most of my time writing automation scripts (python or bash) and learning cool stuff.