PipeTracker is an inventory management application specifically designed for plumbers. The project was born out of the real-world challenges of a self-employed plumber. Built with a user-centered approach, PipeTracker manages inventory for plumbing businesses and simplifies the invoicing process for plumbers.
Here is the link to the deployed project.
\ Am I Responsive Screenshot
I am aware that the application is quite extensive and might seem complicated to use. This is due to the fact that it was created as a real-world application for a real customer. It is not designed to be used by just anyone, but by plumbers and employees of plumbing businesses.
The application is shared by all users, so everyone can see, edit and delete all the stock items and delivery notes, only the basket is personal.
In a real-world scenario, new users of the application would be trained to use the application. To facilitate working with the application, I added an introduction to the most important features to the dashboard. This way new users can get an overview of the application.
Introduction for new users
I structured my planning phase using the 5 UX planes - strategy, scope, structure, skeleton, and surface. The planning process was iterative. While gaining a better understanding of the project's scope and discussing my customer's needs for the application, these planes evolved, creating a user-centered design for PipeTracker.
My initial idea for this project was to create an application for my husband´s plumbing business. He is self-employed and I created his Logo, Designs and Website. So I wanted to make a real-world application where I could put this to use.
After talking to him about possible applications he might need that would be suitable for this project, the idea of an inventory tool - PipeTracker - quickly came to life. In the following parts of the readme, I will refer to my husband as my customer, as the application is built with his real-world needs in mind.
As this project is intended to be a specific real-world application for my husband, he and possible employees are my target audience or customers. But in general, a tool like this could be used for all plumbing companies so a few characteristics for my target audience would be the following.
To get a better understanding of what the application will look like I wrote down the features and sorted them into necessary and nice-to-have features.
From the features defined above I was able to create epics and break these down into user stories.
User Story | Priority |
---|---|
As a new user, I want to register to the application, so that I can manage my inventory. | MUST HAVE |
As an existing user, I want to log in to the application, so that I can access my inventory. | MUST HAVE |
As a site owner, I want to manage user roles, so that I can choose who can access sensitive information. | COULD HAVE |
User Story | Priority |
---|---|
As a site user, I want to create and view categories, so that I can organize my inventory. | MUST HAVE |
As a site user, I want to update categories, so that I can change the structure of my inventory. | MUST HAVE |
As a site user, I want to delete categories, so that I can remove unnecessary categories. | MUST HAVE |
As a site user, I want to create and view stock items, so that I can organize my inventory. | MUST HAVE |
As a site user, I want to update stock items, so that I can correctly maintain my inventory. | MUST HAVE |
As a site user, I want to delete stock items, so that I can remove items from my inventory. | MUST HAVE |
User Story | Priority |
---|---|
As a site user, I want to have a dashboard with statistics, so that I can make decisions for my business. | SHOULD HAVE |
User Story | Priority |
---|---|
As a site user, I want to create and view delivery notes, so that I can track where my stock items went. | MUST HAVE |
As a site user, I want to link customers to delivery notes, so that I can easily invoice the needed items for a customer. | SHOULD HAVE |
As a site user, I want to update delivery notes, so that I can track if it was already invoiced. | MUST HAVE |
As a site user, I want to delete delivery notes, so that I can remove unnecessary delivery notes. | COULD HAVE |
User Story | Priority |
---|---|
As a site user, I want to add stock items to the cart, so that I can collect the items I need. | MUST HAVE |
As a site user, I want to view my cart, so that I can see my collected items. | MUST HAVE |
As a site user, I want to update my cart, so that I can change the number of added stock items. | COULD HAVE |
As a site user, I want to transfer my cart to a delivery note, so that I can easily fill my delivery notes. | MUST HAVE |
As described in the features section, I decided to rename Cart to Basket. Apart from this name change, the user stories were implemented as planned.
User Story | Priority |
---|---|
As a site user, I want to export a delivery note with its items, so that I can easily create an invoice for the customer. | SHOULD HAVE |
As a site user, I want to update stock items by scanning a QR code, so that I can simplify the process of adding and removing items to/from my inventory. | COULD HAVE |
As a site user, I want to import CSV files, so that I can automatically create stock items. | COULD HAVE |
When trying to implement the User Story for Datanorm files, I came to the conclusion, that the user story is not implementable the way I planned it. The file format I wanted to use was created specifically for German plumbing retailers. To create and use such files, a paid tool is needed.
So I decided to rewrite my user story and instead upload CSV files to automatically create stock items.
I created wireframes for desktop, tablet and mobile for each of the main parts of the application.
After discussing the needed features for the application, I started creating my database schema. With every iteration of talking to my customer about the needed features and stored information, the schema grew. The final adaptions were made while creating the wireframes for the application.
As it might be necessary for my customer to create subcategories for categories, I decided to follow the approach of this article about categories and subcategories. I added a parent_id field with a default of 0 to Category. If the parent_id of a category matches the id of another category instead of 0, it is a subcategory.
While creating the database schema I was also thinking about how I will implement the cart. At first, I was not sure if I should add a Cart table or use Local Storage to store stock items in the cart. However, I decided to add a table because it is likely that my customer will access the application using different devices. So to ensure the cart is stored and always up-to-date on each device, I will save the cart information in the database instead of local storage.
\ Database Schema for PipeTracker
After finishing my project, I exported an ERD from Django following this tutorial. It shows my custom apps delivery, stock and basket with the created tables as well as other necessary relations, like the tables created by the allauth package.
\ Final ERD
As mentioned above, I already created a website, as well as all the designs and logos for my customer´s company before starting this project. I will use the existing design for this project. As a reference, here is what the company´s business card I designed looks like.
\ Existing design created by myself
I used shades of blue and the background representing water as it is fitting for a plumbing company. The logo is my customer´s name with the services he offers.
From this existing design, I created the color scheme for this project. I used contrast-grid.eightshapes.com to check the contrast and possible color combinations.
\ Accessibility/contrast check for color scheme
I will use the same fonts for this project as I used on the website. Roboto and Montserrat are both Google Fonts and fit well with the logo and design of the company.
I also used the existing logo to create a new logo for PipeTracker.
\ PipeTracker Logo
CRUD (Create, Read, Update and Delete) is available for many features of PipeTracker. Below is a table showing the features providing (full) CRUD functionality.
Feature | Create | Read | Update | Delete |
---|---|---|---|---|
Category | ✓ | ✓ | ✓ | ✓ (only for superuser) |
Stock Item | ✓ | ✓ | ✓ | ✓ |
Delivery Note | ✓ | ✓ | ✓ | ✓ |
Customer | ✓ | ✓ (when adding a delivery note) | only in the admin panel | only in the admin panel |
Basket | The basket is automatically created for each user. | ✓ | ✓ | The basket can be empty and items can be removed from it. |
Because of the nature of my application, it does not make much sense to have features available to logged-out users. So I created a landing page with a brief explanation of what the application is about and a link to register or login.
\ Screenshot of the landing page
The AllAuth and Crispy Forms packages are used for authentication. I added custom CSS using Tailwind classes to adapt the forms to my design.
To create an account, users are required to add their email, username and password. Password requirements are shown and in the case of invalid input, the user gets clear feedback.
\ Screenshot of the Sign Up page
\ Screenshot of the Login page
After successfully logging in, the user is redirected to the Dashboard and a success message is showing. The available pages and a logout link with the username of the currently logged-in user are shown in the header.
\ Screenshot of the Logout page
Success and error messages are shown to the user for example after logging in or out, after adding, updating or deleting an element or if a user tries to edit a closed delivery note.
\ Success message after creating a category
\ Error message after trying to edit a closed delivery note
The messages can be closed using the icon, after 4 seconds they are closed automatically.
I added custom error pages for the 404, 500, 400 and 403 errors. The error pages include the site's header and footer. Depending on whether the user is logged in or not, the pages include a link to the dashboard or to register and log in.
\ Custom 404 error page (mobile)
\ Custom 500 error page (mobile)
\ Custom 400 error page (mobile)
\ Custom 403 error page (mobile)
Stock items are grouped into categories, to list the items, the user must first choose a category. The search bar is an alternative to quickly find a specific item. The matchcode, size or name of the stock item can be entered and all matching results are shown in a table, using the same template as the normal stock items table.
\ Stock Items search results (tablet)
After clicking Stock Items in the menu, all available categories are shown as cards. They can be opened, edited or deleted.
\ Categories with CRUD
Each category can contain either stock items or one or many subcategories, not both. If the selected category has one or many subcategories, they are shown and breadcrumbs appear at the top left to indicate the levels of categories and subcategories.
\ Subcategories with CRUD
When editing a category, only superusers have the option to delete the category, the button is not rendered for other users. If a normal user enters the URL to delete a category, the permission is denied.
\ Edit a category (superuser)
\ Permission denied delete category (normal user)
A new category or stock item can be added at each of the Stock Items pages. When adding a stock item, only categories with no sub-categories can be chosen and when adding a new category, only categories with no stock items can be chosen as a parent category.
\ Add a stock item (mobile)
If the selected category does not have any subcategories, the stock items of this category are shown. The items are presented in a table that is orderable by relevant fields.
Full CRUD is also available for stock items. By clicking edit, the selected stock item can be edited or deleted. The item quantity can also be edited directly in the table by clicking - or +. The stock item can be added to the basket by choosing the desired amount and clicking Add. There can never be added more items to the basket, than available in the stock.
\ Stock Items table
\ Edit a stock item (tablet)
When opening the detail page of a stock item, a QR code for this page is generated and shown. This QR code can be printed out and placed in the physical stock of the plumber. This way the plumber can easily scan the QR code of the item they want to change or add to a delivery note.
\ QR code for a stock item
When scanning the QR code, provided the user is logged in, the detail page of the according stock item is opened. If the user is not authenticated, the application redirects the user to the login page and after logging in to the scanned item.
\ Stock Item Detail Page (mobile)
A CSV file can be uploaded to automatically create stock items. The form checks the file extension to only allow uploading .csv files. The uploaded file is then validated to check if all the required fields are in the file and if the given values have the correct format.
Any errors in the file are reported to the user. If there are no errors, the stock items are created. The number of created stock items is then printed in a message to the user. If the file contains a stock item that already exists, it is skipped to avoid unintentional changes to the existing stock.
\ CSV Upload
An example file containing the necessary columns can be downloaded, to help the user by providing a validated template.
\ Example CSV file
In my wireframes and User Stories, I called this page Cart. However, after showing the application to some people, I noticed that this name might be confusing, as there is nothing to buy on this page. I changed the name to Basket, to indicate that it is a place to collect stock items before adding them to a delivery note. This is due to my customer telling me, that he will have lots of stock items to add to a delivery note at once. So choosing a delivery note for each item would be a lot of extra steps, especially when there are many delivery notes. So the basket can be used as a collection to quickly add each stock item and when finished, the delivery note can be chosen and the stock items are added to it.
\ Basket
The items that were added to the basket are also presented in a table. The quantity can be changed, if that happens, the quantity of the original stock item is also updated automatically.
Underneath the table, there is a select field to choose a delivery note. Only open delivery notes are available here. I decided to use select2 for this field because I wanted to make it searchable in case there are many open delivery notes. After choosing a delivery note and clicking Add, the items are added to the note and deleted from the basket.
Delivery notes are shown in a table, the columns are orderable and closed delivery notes have a red background. The title of the note can be clicked to open the detail page for the delivery note. Open delivery notes can be edited and have a clickable link in the table. Closed delivery notes can only be edited and deleted by superusers. New delivery notes can be added with the Add button as known from the other pages.
\ Delivery Notes (normal user)
When adding a delivery note, a customer, title and status are required. The user can choose an existing customer or create a new one.
\ Add a delivery note (tablet)
Customers can only be created when creating a delivery note, as they are only important for this step. Customers can not be edited or deleted within the application, only in the administration terminal.
\ Add a customer (tablet)
The detail page of a delivery note contains all the information about the delivery note itself. The status is shown with a red or green circle that is not clickable. On the detail page, the user finds another link to edit the delivery note, if it is open or the user is a superuser.
Underneath the information about the delivery note, a table containing all the stock items added to the delivery note is shown. Similar to the basket table, the quantity of the items can be changed in the table. If that happens, the quantity of the original stock item is also changed.
The button Export as PDF saves the delivery note data with all items to a PDF and downloads the file.
\ Delivery note detail
The exported PDF is really simple but contains all the necessary information.
\ Delivery note Export PDF
Open delivery notes can be edited and deleted by all (registered) users. Closed delivery notes can only be edited or deleted by the superuser.
\ Edit an open delivery note (mobile)
\ Edit a closed delivery note (normal user)
After extensive testing and getting feedback from my customer, I can say that the project is ready to be used as a real-world application.
There is one user story left that I marked as won´t have for this release.
The user story might be implemented for future releases but is not necessary for my customer at this point.
Other adaptions that will be made in a separate repository for my customer to use the application will be:
I used a GitHub Projects Board to plan and document my work. The details of my agile approach can be found in the separate AGILE.md file.
I took various steps to ensure security for this project. As the application is designed to be used by employees of a plumbing business, logging in is required for all actions. I used the login_required decorator for my urlpatterns, this ensures that the user is logged in, before calling the view.
Django AllAuth was used to implement user authentication. The package ensures secure user registration and login. Role-based access control ensures that only a superuser has access to the admin panel. Other roles like plumbers, warehouse employees, etc. are planned for future versions of the application. However, these additional roles are not necessary for the first release.
I used a defensive design approach building this project. User input is validated and error messages provide feedback to the user. Users who are not logged in are redirected to the Login page if they try to access restricted content. When trying to delete data, a confirm page is always used before actually deleting the content. Custom error pages were implemented to stay consistent with the application design, depending on whether the user is logged in, they include links to the Dashboard or the Login/Registration pages.
Cross-Site Request Forgery (CSRF) protection is implemented using CSRF tokens. This prevents unauthorized requests from malicious websites.
I frequently reviewed and improved my code to identify and eliminate security issues. Extensive testing ensures validated features and security. Sensitive information like secret keys and database credentials are stored as environment variables. This ensures that important data remains confidential and is not exposed.
Please refer to the separate TESTING.md file.
Here is the link to the deployed project.
The first step is to create a new repository, using the Code Institute Template. After creating the repository, you can open it in the IDE of your choice.
If you choose to work in a local IDE, it is important to create a virtual environment before continuing. I am using PyCharm, where the local environment can be conveniently set up by adding a new interpreter. Another way is by typing python -m venv .venv
in the terminal.
pip install django gunicorn
pip install dj_database_url psycopg2
pip install dj3-cloudinary-storage
pip freeze --local > requirements.txt
django-admin startproject <name>
(in my case <name>
was pipetracker)python manage.py runserver
You can use a database of your choice, following are the instructions if you use ElephantSQL.
In the settings.py file add this code:
import os
import dj_database_url
if os.path.isfile("env.py"):
import env
SECRET_KEY = os.environ.get("SECRET_KEY")
DEBUG = "DEVELOPMENT" in os.environ
DATABASES = {
'default': dj_database_url.parse(os.environ.get("DATABASE_URL"))
}
In the env.py file add this code and ensure the file is added to .gitignore:
import os
os.environ["SECRET_KEY"] = "addSecretKeyHere"
os.environ["DEVELOPMENT"] = "TRUE"
os.environ["DATABASE_URL"]= "copiedDatabaseURL"
os.environ["CLOUDINARY_URL"] = "copiedCloudinaryURL"
python manage.py migrate
to migrate your database structure to the ElephantSQL database.In the settings.py file add this code to link to Cloudinary:
INSTALLED_APPS = [...
'cloudinary_storage',
'django.contrib.staticfiles',
'cloudinary',
...]
# NOTE: the second line should already be in the file, add the line above and below, the order is important)
STATIC_URL = 'static/'
STATICFILES_STORAGE = 'cloudinary_storage.storage.StaticHashedCloudinaryStorage'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_URL = '/media/'
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
Code for templates directory in settings.py:
TEMPLATES_DIR = os.path.join(BASE_DIR, 'templates')
'DIRS': [TEMPLATES_DIR] # in existing TEMPLATES variable
ALLOWED_HOSTS = ["PROJECT_NAME.herokuapp.com", "localhost"]
in settings.pyweb gunicorn pipetracker.wsgi
to ProcfileForking creates a copy of the project on GitHub. Follow these steps to fork this repository:
I decided to use Tailwind for this project. I already knew Bootstrap from previous experience but did not know anything about Tailwind. So I decided to use this project as an opportunity to get to know it. After some initial installation troubles, I really began to like it and its utility-first approach.