Python Revision Project - 2

Simple CLI inventory management system

Project Description


This is a CLI based simple inventory management system. Users can view, find, create, restock, buy and return items. It also manages a till balance that is used in a POS system that is required while making transactions like buying and returning items. The code for this project can be found in this repository.

Project Detail


For this project, I have not maintained a .txt file to store the data as compared to the previous project. Rather, I have created a dictionary that contains the quantity and price of the item with item name as the key of the dictionary. I wanted to revise my knowledge of dictionaries in python because it is a bit different than JSON object that I'm used to dealing with, while working as a frontend developer.

The project structure is easy to follow as well:

InventoryManagement/
│
├── Inventory.py
├── main.py
└── util.py

The project is initialized through main.py . It allows user to input simple prompts through which they can interact with the inventory management system in the CLI. The util.py contains a simple function that capitalizes the first letter of each word. I just wanted to challenge myself a bit extra so I made this 🤣. And finally, Inventory.py contains two classes Inventory and PointOfService which contains the logic to manipulate the inventory.

Starting with util.py :

# util.py
def capitalizeFirstLetter(value: str):
    capitalizedArr = []
    valueArr = value.split(" ")

    for word in valueArr:
        letterArr = []
        for index, letter in enumerate(word):
            if(index == 0):
                letterArr.append(letter.upper())
            else:
                letterArr.append(letter.lower())
        capitalizedArr.append("".join(letterArr))

    finalString = " ".join(capitalizedArr)
    return finalString

The logic here is simple, it takes a string and splits it for every " " space there is. So for example, "lazy fox" would be ["lazy", "fox"] . Then for every word in the array an array called letterArr is maintained. And in the inner loop, the first letter of the word is capitalized and appended to the array where as other letters are decapitalized and appended. So for the word "lazy", the array would contain ["L", "a", "z", "y"] .

Then, the letters are re-joined to create the initial word. And finally, all of the words are joined with space in between to create the original value with first letter capitalized. You can check this process step by step either by debugging or using Jupyter notebook if you like.

Then moving on to main.py :

# main.py
from Inventory import Inventory
from Inventory import PointOfService
from util import capitalizeFirstLetter

inventory = Inventory()
inventory.seedItems()

pos = PointOfService(inventory, 1000)

userLoggedIn = True

print('Welcome to Inventory Management System!')

while(userLoggedIn):
    userPrompt = input("What would you like to do?\n1. View items\n2. Create an item\n3. Add item to inventory\n4. Find item\n5. Delete item\n6. Get total stock\n7. Purchase item\n8. Return item\n9. Exit the program\nEnter: ")

    if(userPrompt == '1'):
        inventory.browseItems()
        print('')
    elif(userPrompt == '2'):
        itemName = input('Enter item name: ')
        itemQuantity = input('Enter item quantity: ')
        itemPrice = input('Enter item price: ')
        print(inventory.createItem(capitalizeFirstLetter(itemName), int(itemQuantity), float(itemPrice)))
    elif(userPrompt == '3'):
        itemName = input('Enter item name: ')
        itemQuantity = input('Enter additional item quantity: ')
        print(inventory.addItem(capitalizeFirstLetter(itemName), int(itemQuantity)))
    elif(userPrompt == '4'):
        itemName = input('Enter item name: ')
        print(inventory.findItem(capitalizeFirstLetter(itemName)))
    elif(userPrompt == '5'):
        itemName = input('Enter item name to delete: ')
        inventory.deleteItem(capitalizeFirstLetter(itemName))
    elif(userPrompt == '6'):
        print(inventory.totalStock())
    elif(userPrompt == '7'):
        itemName = input('Enter item name: ')
        itemQuantity = input('Enter item quantity: ')
        print(pos.purchaseItem(capitalizeFirstLetter(itemName), int(itemQuantity)))
    elif(userPrompt == '8'):
        itemName = input('Enter item name: ')
        itemQuantity = input('Enter item quantity: ')
        pos.returnItem(capitalizeFirstLetter(itemName), int(itemQuantity))
    elif(userPrompt == '9'):
        userLoggedIn = False

As mentioned before, it only contains prompts that allows user to interact with the system. At first, object is created for class Inventory and seedItems() method was called. Then, another object for class PointOfService is also created with initial till amount set to 1000 . Then, for respective prompts respective methods are used passing required arguments.

Finally, Inventory.py : (Do catch a break as this was a long read 🤣)

# Inventory.py
class Inventory:
    def __init__(self):
        self.items = {}

    def seedItems(self):
        '''Default items'''
        defaultItemsSnacks = ['Oreo', 'Cheetos', 'Doritos']
        defaultItemsDrinks = ['Coke', 'Pepsi', 'Fanta']

        for item in defaultItemsSnacks:
            self.items[item] = {'quantity': 50, 'price': 15}

        for item in defaultItemsDrinks:
            self.items[item] = {'quantity': 30, 'price': 10}

    def browseItems(self):
        '''Display all items'''
        for item in self.items:
            print(f"Name: {item}, Quantity: {self.items[item]['quantity']}, Price: {self.items[item]['price']}")

    def createItem(self, name, quantity, price):
        '''Create new item'''
        if name not in self.items:
            self.items[name] = {'quantity': quantity, 'price': price}
            return "Item created successfully!\n"
        else:
            return f"Item with name '{name}' already exists!\n"

    def addItem(self, name, quantity):
        '''Update item stock quantity'''
        if name in self.items:
            self.items[name]['quantity'] += quantity
            return "Quantity updated successfully!\n"
        else:
            return f"No item with name '{name}' exists!\n"

    def findItem(self, name):
        '''Search item's detail'''
        if name in self.items:
            item = self.items[name]
            return f"Item name: {name}, Available Quantity: {item['quantity']}, Price: ${item['price']}\n"
        else:
            return f"No item with name '{name}' exists!\n"

    def findItemPOS(self, name: str):
        '''Returns item'''
        if name in self.items:
            item = self.items[name]
            return item
        else:
            return None

    def udpateItem(self, name, newQuantity):
        '''Update stock quantity'''
        if name in self.items:
            self.items[name]['quantity'] = newQuantity
        else:
            return f"No item with name '{name}' exists!\n"

    def deleteItem(self, name):
        '''Remove item'''
        if name in self.items:
            del self.items[name]
            return f"Item '{name}' removed successfully!\n"
        else:
            return f"No item with name '{name}' exists!\n"

    def totalStock(self):
        '''Total items in stock'''
        sum = 0
        for item in self.items:
            sum = sum + self.items[item]['quantity']
        return f"There are in total {sum} items in stock.\n"

class PointOfService:
    def __init__(self, inventory: Inventory, tillBalance=0):
        self.inventory = inventory
        self.tillBalance = tillBalance

    def purchaseItem(self, name, quantity):
        item = self.inventory.findItemPOS(name)
        if item:
            if(item['quantity'] >= quantity):
                totalCost = quantity * item['price']
                if(totalCost <= self.tillBalance):
                    self.tillBalance -= totalCost
                    self.inventory.udpateItem(name, item['quantity'] - quantity)
                    return f"Transaction successful. Total cost: ${totalCost}\n"
                else:
                    return 'Insufficient funds in the till!\n'
            else:
                return 'Insufficient quantity in stock!\n'
        else:
            return 'Item not found!\n'

    def returnItem(self, name, quantity):
        item = self.inventory.findItemPOS(name)
        if item:
            self.tillBalance += item['price'] * quantity
            self.inventory.udpateItem(name, item['quantity'] + quantity)
            return f"Return successful. Refund: ${item['price'] * quantity}\n"
        else:
            return "Item not found in inventory.\n"

Brief description of each method can be found below:

class Inventory :

  • __init__() : Constructor for the class. Initializes attribute named items which is a dictionary.

  • seedItems() : It simply inserts default items so that when the user executes the main.py file, there are some items to interact with.

  • browseItems() : Displays every item with its name, quantity and price.

  • createItem() : It creates new item based on the arguments provided by the user i.e., name, quantity and price of the item. If the item already exists then appropriate message is displayed.

  • addItem() : It restocks the items i.e., increases the existing quantity by adding in the provided quantity.

  • findItem() : User can search the item, and its details are returned. If no item with the name exists, appropriate message is displayed.

  • findItemPOS() : It returns the item itself if it exists and returns None if it does not exist.

  • updateItem() : Updates the quantity of the item with the new quantity provided through the parameter.

  • deleteItem() : Removes the item from the dictionary.

  • totalStock() : Displays the total number of items in stock.

class PointOfService :

  • __init__() : Constructor for the class. Initializes attribute inventory and tillBalance.

  • purchaseItem() : The item name and quantity provided by the user as the prompt is processed here. The requested quantity is deducted with the total quantity in stock to update the stock quantity and total cost is calculated and displayed to the user. If there is no item, requested amount is greater than in stock and till balance is less than total cost then appropriate error messages are shown.

  • returnItem() : The returned item with its quantity is added back to the stock.

Insights


Through this project, I was able to revise the manipulation of dictionary in python. The program however is not perfect and further improvements can definitely be made. You are welcome to clone or fork the project and make improvements to it for your own practice💪.

Conclusion


Concluding, python dictionary and arrays are very useful to group data together. Without dictionary, managing and manipulating even such simple data would be a hassle. Now, for the next project I plan to experiment with data frames. See ya later 👋.